From 8774100508cc36b5b0b96a4d78001543cdbe2f9f Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 28 Apr 2017 13:22:55 +0100 Subject: [PATCH 001/416] 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 002/416] 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 003/416] 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 004/416] 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 005/416] 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 006/416] 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 007/416] 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 008/416] 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 009/416] 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 010/416] 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 011/416] 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 ad2ed129800bbcc79544c66426f2fd70a5708dfa Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 10 May 2017 14:22:17 +0100 Subject: [PATCH 012/416] 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 013/416] 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 6326a95b39f335d3da3b66bb48a53a9d456c208c Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 11 May 2017 17:04:11 +0100 Subject: [PATCH 014/416] 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 015/416] 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 8725ef38639573bf6f0b36d6ee6cac305e5ef70a Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 11 May 2017 17:47:45 +0100 Subject: [PATCH 016/416] 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 017/416] 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 018/416] 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 019/416] 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 020/416] 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 021/416] 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 022/416] 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 da3cb0ee48aaea420a31fd9e060e159dfd7e910f Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 15 May 2017 14:52:19 +0100 Subject: [PATCH 023/416] 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 024/416] 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 025/416] 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 026/416] 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 027/416] 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 028/416] 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 029/416] 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 030/416] 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 031/416] 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 032/416] 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 2c5fb01f03b9ac5e98f2e488c1a6acb26c7e5d22 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 16 May 2017 14:13:22 +0100 Subject: [PATCH 033/416] 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 034/416] 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 e1089574ae02bee6036cb745513c026381558f43 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 17 May 2017 09:46:17 +0100 Subject: [PATCH 035/416] Write some tests for the RTS UI Add tests that make assertions about the UI during registration when registration is done with a user recognised as a team member (by the mock rtsClient). --- .../structures/login/Registration.js | 3 +- .../structures/login/Registration-test.js | 105 ++++++++++++++++++ .../views/login/RegistrationForm-test.js | 86 ++++++++++++++ test/test-utils.js | 14 +++ 4 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 test/components/structures/login/Registration-test.js create mode 100644 test/components/views/login/RegistrationForm-test.js diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index 5501a39b58..5eecfa5ff6 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -98,7 +98,7 @@ module.exports = React.createClass({ this.props.teamServerConfig.teamServerURL && !this._rtsClient ) { - this._rtsClient = new RtsClient(this.props.teamServerConfig.teamServerURL); + this._rtsClient = this.props.rtsClient || new RtsClient(this.props.teamServerConfig.teamServerURL); this.setState({ teamServerBusy: true, @@ -221,7 +221,6 @@ module.exports = React.createClass({ } trackPromise.then((teamToken) => { - console.info('Team token promise',teamToken); this.props.onLoggedIn({ userId: response.user_id, deviceId: response.device_id, diff --git a/test/components/structures/login/Registration-test.js b/test/components/structures/login/Registration-test.js new file mode 100644 index 0000000000..b4b54a6315 --- /dev/null +++ b/test/components/structures/login/Registration-test.js @@ -0,0 +1,105 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const React = require('react'); +const ReactDOM = require('react-dom'); +const ReactTestUtils = require('react-addons-test-utils'); +const expect = require('expect'); + +const testUtils = require('test-utils'); + +const sdk = require('matrix-react-sdk'); +const Registration = sdk.getComponent('structures.login.Registration'); + +let rtsClient; +let client; + +const TEAM_CONFIG = { + supportEmail: 'support@some.domain', + teamServerURL: 'http://someteamserver.bla', +}; + +const CREDENTIALS = {userId: '@me:here'}; +const MOCK_REG_RESPONSE = { + user_id: CREDENTIALS.userId, + device_id: 'mydevice', + access_token: '2234569864534231', +}; + +describe('Registration', function() { + beforeEach(function() { + testUtils.beforeEach(this); + client = testUtils.createTestClient(); + client.credentials = CREDENTIALS; + + // Mock an RTS client that supports one team and naively returns team tokens when + // tracking by mapping email SIDs to team tokens. This is fine because we only + // want to assert the client behaviour such that a user recognised by the + // rtsClient (which would normally talk to the RTS server) as a team member is + // correctly logged in as one (and other such assertions). + rtsClient = testUtils.createTestRtsClient( + { + 'myawesometeam123': { + name: 'Team Awesome', + domain: 'team.awesome.net', + }, + }, + {'someEmailSid1234': 'myawesometeam123'}, + ); + }); + + it('should track a referral following successful registration of a team member', function(done) { + const expectedCreds = { + userId: MOCK_REG_RESPONSE.user_id, + deviceId: MOCK_REG_RESPONSE.device_id, + homeserverUrl: client.getHomeserverUrl(), + identityServerUrl: client.getIdentityServerUrl(), + accessToken: MOCK_REG_RESPONSE.access_token, + }; + const onLoggedIn = function(creds, teamToken) { + expect(creds).toEqual(expectedCreds); + expect(teamToken).toBe('myawesometeam123'); + done(); + }; + + const res = ReactTestUtils.renderIntoDocument( + , + ); + + res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someEmailSid1234'}); + }); + + it('should NOT track a referral following successful registration of a non-team member', function(done) { + const onLoggedIn = expect.createSpy().andCall(function(creds, teamToken) { + expect(teamToken).toNotExist(); + done(); + }); + + const res = ReactTestUtils.renderIntoDocument( + , + ); + + res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someOtherEmailSid11'}); + }); +}); diff --git a/test/components/views/login/RegistrationForm-test.js b/test/components/views/login/RegistrationForm-test.js new file mode 100644 index 0000000000..81db5b487b --- /dev/null +++ b/test/components/views/login/RegistrationForm-test.js @@ -0,0 +1,86 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const React = require('react'); +const ReactDOM = require("react-dom"); +const ReactTestUtils = require('react-addons-test-utils'); +const expect = require('expect'); + +const testUtils = require('test-utils'); + +const sdk = require('matrix-react-sdk'); +const RegistrationForm = sdk.getComponent('views.login.RegistrationForm'); + +const TEAM_CONFIG = { + supportEmail: "support@some.domain", + teams: [ + { name: "The Team Org.", domain: "team.ac.uk" }, + { name: "The Super Team", domain: "superteam.ac.uk" }, + ], +}; + +function doInputEmail(inputEmail, onTeamSelected) { + const res = ReactTestUtils.renderIntoDocument( + , + ); + + const teamInput = res.refs.email; + teamInput.value = inputEmail; + + ReactTestUtils.Simulate.change(teamInput); + ReactTestUtils.Simulate.blur(teamInput); + + return res; +} + +function expectTeamSelectedFromEmailInput(inputEmail, expectedTeam) { + const onTeamSelected = expect.createSpy(); + doInputEmail(inputEmail, onTeamSelected); + + expect(onTeamSelected).toHaveBeenCalledWith(expectedTeam); +} + +function expectSupportFromEmailInput(inputEmail, isSupportShown) { + const onTeamSelected = expect.createSpy(); + const res = doInputEmail(inputEmail, onTeamSelected); + + expect(res.state.showSupportEmail).toBe(isSupportShown); +} + +describe('RegistrationForm', function() { + beforeEach(function() { + testUtils.beforeEach(this); + }); + + it('should select a team when a team email is entered', function() { + expectTeamSelectedFromEmailInput("member@team.ac.uk", TEAM_CONFIG.teams[0]); + }); + + it('should not select a team when an unrecognised team email is entered', function() { + expectTeamSelectedFromEmailInput("member@someunknownteam.ac.uk", null); + }); + + it('should show support when an unrecognised team email is entered', function() { + expectSupportFromEmailInput("member@someunknownteam.ac.uk", true); + }); + + it('should NOT show support when an unrecognised non-team email is entered', function() { + expectSupportFromEmailInput("someone@yahoo.com", false); + }); +}); diff --git a/test/test-utils.js b/test/test-utils.js index 9f404f98eb..2c866d345c 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -137,6 +137,20 @@ export function createTestClient() { }; } +export function createTestRtsClient(teamMap, sidMap) { + return { + getTeamsConfig() { + return q(Object.keys(teamMap).map((token) => teamMap[token])); + }, + trackReferral(referrer, emailSid, clientSecret) { + return q({team_token: sidMap[emailSid]}); + }, + getTeam(teamToken) { + return q(teamMap[teamToken]); + }, + }; +} + /** * Create an Event. * @param {Object} opts Values for the event. From 96c3bf56f8dbbbcd242bee9292ef87085414c63e Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 19 May 2017 09:43:56 +0100 Subject: [PATCH 036/416] Implement warm-fuzzy success dialog for SetMxIdDialog --- src/components/views/dialogs/SetMxIdDialog.js | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 86b5fccbc2..d9d07d517b 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -53,6 +53,9 @@ export default React.createClass({ doingUIAuth: false, // Indicate error with auth authError: '', + + // Indicate success of setting mxid + success: false, }; }, @@ -95,6 +98,10 @@ export default React.createClass({ }); }, + onSuccessContinue: function() { + this.props.onFinished(true, this._registeredCreds); + }, + _doUsernameCheck: function() { // Check if username is available return this._matrixClient.isUsernameAvailable(this.state.username).then( @@ -162,7 +169,7 @@ export default React.createClass({ // XXX Implement RTS /register here const teamToken = null; - this.props.onFinished(true, { + this._registeredCreds = { userId: response.user_id, deviceId: response.device_id, homeserverUrl: this._matrixClient.getHomeserverUrl(), @@ -170,6 +177,11 @@ export default React.createClass({ accessToken: response.access_token, password: this._generatedPassword, teamToken: teamToken, + }; + + // Before continuing, show a warm-fuzzy success and only submit onSuccessContinue + this.setState({ + success: true, }); }, @@ -219,6 +231,30 @@ export default React.createClass({ !this.state.usernameError && !this.state.usernameBusy; + if (this.state.success) { + return ( + +
+

+ You have successfully + picked { this.state.username } as your + username and you now have access to the full + set of features on Riot. +

+
+
+ +
+
+ ); + } + return ( Date: Mon, 22 May 2017 14:46:49 +0100 Subject: [PATCH 037/416] Add prop to toggle whether new password input is autoFocused --- src/components/views/settings/ChangePassword.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 601b774932..bfc9ac264e 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -35,6 +35,8 @@ module.exports = React.createClass({ rowInputClassName: React.PropTypes.string, buttonClassName: React.PropTypes.string, confirm: React.PropTypes.bool, + // Whether to autoFocus the new password input + autoFocusNewPasswordInput: React.PropTypes.bool, }, Phases: { @@ -199,7 +201,7 @@ module.exports = React.createClass({
- +
From b0a824c94190c317f6c18a238d6f3e04727d58a6 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 22 May 2017 16:28:23 +0100 Subject: [PATCH 038/416] Remove double declaration of TextInputDialog --- src/components/structures/MatrixChat.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 85c12979f6..59ce1b622d 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -439,7 +439,6 @@ module.exports = React.createClass({ break; } - var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog"); Modal.createDialog(TextInputDialog, { title: "Create Room", description: "Room name (optional)", From 298c5e4df32d87b0707b9f71ff489e12b800fe15 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 24 May 2017 16:56:13 +0100 Subject: [PATCH 039/416] Implement a store for RoomView This allows for a truely flux-y way of storing the currently viewed room, making some callbacks (like onRoomIdResolved) redundant and making sure that the currently viewed room (ID) is only stored in one place as opposed to the previous many places. This was required for the `join_room` action which can be dispatched to join the currently viewed room. Another change was to introduce `LifeCycleStore` which is a start at encorporating state related to the lifecycle of the app into a flux store. Currently it only contains an action which will be dispatched when the sync state has become PREPARED. This was necessary to do a deferred dispatch of `join_room` following the registration of a PWLU (PassWord-Less User). The following actions are introduced: - RoomViewStore: - `view_room`: dispatch to change the currently viewed room ID - `join_room`: dispatch to join the currently viewed room - LifecycleStore: - `do_after_sync_prepared`: dispatch to store an action which will be dispatched when `sync_state` is dispatched with `state = 'PREPARED'` - MatrixChat: - `sync_state`: dispatched when the sync state changes. Ideally there'd be a SyncStateStore that emitted an `update` upon receiving this, but for now the `LifecycleStore` will listen for `sync_state` directly. --- src/components/structures/LoggedInView.js | 5 +- src/components/structures/MatrixChat.js | 38 ++-- src/components/structures/RoomView.js | 212 ++++++------------ src/components/views/dialogs/SetMxIdDialog.js | 1 + src/createRoom.js | 15 +- src/stores/LifecycleStore.js | 73 ++++++ src/stores/RoomViewStore.js | 145 ++++++++++++ src/stores/SessionStore.js | 15 ++ test/components/structures/RoomView-test.js | 67 ------ test/stores/RoomViewStore-test.js | 56 +++++ test/test-utils.js | 13 +- 11 files changed, 399 insertions(+), 241 deletions(-) create mode 100644 src/stores/LifecycleStore.js create mode 100644 src/stores/RoomViewStore.js delete mode 100644 test/components/structures/RoomView-test.js create mode 100644 test/stores/RoomViewStore-test.js diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index e559a21e1a..df24fbb33b 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -40,7 +40,6 @@ export default React.createClass({ propTypes: { matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired, page_type: React.PropTypes.string.isRequired, - onRoomIdResolved: React.PropTypes.func, onRoomCreated: React.PropTypes.func, onUserSettingsClose: React.PropTypes.func, @@ -190,16 +189,14 @@ export default React.createClass({ case PageTypes.RoomView: page_element = { modal.close(); - if (this.currentRoomId === roomId) { + if (this.state.currentRoomId === roomId) { dis.dispatch({action: 'view_next_room'}); } }, (err) => { @@ -807,8 +808,12 @@ module.exports = React.createClass({ this._teamToken = teamToken; dis.dispatch({action: 'view_home_page'}); } else if (this._is_registered) { + this._is_registered = false; if (this.props.config.welcomeUserId) { - createRoom({dmUserId: this.props.config.welcomeUserId}); + createRoom({ + dmUserId: this.props.config.welcomeUserId, + andView: false, + }); return; } // The user has just logged in after registering @@ -853,7 +858,6 @@ module.exports = React.createClass({ ready: false, collapse_lhs: false, collapse_rhs: false, - currentRoomAlias: null, currentRoomId: null, page_type: PageTypes.RoomDirectory, }); @@ -891,6 +895,7 @@ module.exports = React.createClass({ }); cli.on('sync', function(state, prevState) { + dis.dispatch({action: 'sync_state', prevState, state}); self.updateStatusIndicator(state, prevState); if (state === "SYNCING" && prevState === "SYNCING") { return; @@ -1102,6 +1107,8 @@ module.exports = React.createClass({ }, onRegistered: function(credentials, teamToken) { + // XXX: These both should be in state or ideally store(s) because we risk not + // rendering the most up-to-date view of state otherwise. // teamToken may not be truthy this._teamToken = teamToken; this._is_registered = true; @@ -1163,13 +1170,6 @@ module.exports = React.createClass({ } }, - onRoomIdResolved: function(roomId) { - // It's the RoomView's resposibility to look up room aliases, but we need the - // ID to pass into things like the Member List, so the Room View tells us when - // its done that resolution so we can display things that take a room ID. - this.setState({currentRoomId: roomId}); - }, - _makeRegistrationUrl: function(params) { if (this.props.startingFragmentQueryParams.referrer) { params.referrer = this.props.startingFragmentQueryParams.referrer; @@ -1211,10 +1211,10 @@ module.exports = React.createClass({ const LoggedInView = sdk.getComponent('structures.LoggedInView'); return ( { this.forceUpdate(); - } + }, }); - if (this.props.roomAddress[0] == '#') { - // we always look up the alias from the directory server: - // we want the room that the given alias is pointing to - // right now. We may have joined that alias before but there's - // no guarantee the alias hasn't subsequently been remapped. - MatrixClientPeg.get().getRoomIdForAlias(this.props.roomAddress).done((result) => { - if (this.props.onRoomIdResolved) { - this.props.onRoomIdResolved(result.room_id); - } - var room = MatrixClientPeg.get().getRoom(result.room_id); - this.setState({ - room: room, - roomId: result.room_id, - roomLoading: !room, - unsentMessageError: this._getUnsentMessageError(room), - }, this._onHaveRoom); - }, (err) => { - this.setState({ - roomLoading: false, - roomLoadError: err, - }); - }); - } else { - var room = MatrixClientPeg.get().getRoom(this.props.roomAddress); - this.setState({ - roomId: this.props.roomAddress, - room: room, - roomLoading: !room, - unsentMessageError: this._getUnsentMessageError(room), - }, this._onHaveRoom); + // Start listening for RoomViewStore updates + RoomViewStore.addListener(this._onRoomViewStoreUpdate); + this._onRoomViewStoreUpdate(true); + }, + + _onRoomViewStoreUpdate: function(initial) { + if (this.unmounted) { + return; } + this.setState({ + roomId: RoomViewStore.getRoomId(), + roomAlias: RoomViewStore.getRoomAlias(), + joining: RoomViewStore.isJoining(), + joinError: RoomViewStore.getJoinError(), + }, () => { + this._onHaveRoom(); + this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId)); + }); }, _onHaveRoom: function() { @@ -224,17 +202,17 @@ module.exports = React.createClass({ // NB. We peek if we are not in the room, although if we try to peek into // a room in which we have a member event (ie. we've left) synapse will just // send us the same data as we get in the sync (ie. the last events we saw). - var user_is_in_room = null; - if (this.state.room) { - user_is_in_room = this.state.room.hasMembershipState( - MatrixClientPeg.get().credentials.userId, 'join' + const room = MatrixClientPeg.get().getRoom(this.state.roomId); + let isUserJoined = null; + if (room) { + isUserJoined = room.hasMembershipState( + MatrixClientPeg.get().credentials.userId, 'join', ); - this._updateAutoComplete(); - this.tabComplete.loadEntries(this.state.room); + this._updateAutoComplete(room); + this.tabComplete.loadEntries(room); } - - if (!user_is_in_room && this.state.roomId) { + if (!isUserJoined && !this.state.joining && this.state.roomId) { if (this.props.autoJoin) { this.onJoinButtonClicked(); } else if (this.state.roomId) { @@ -260,9 +238,12 @@ module.exports = React.createClass({ } }).done(); } - } else if (user_is_in_room) { + } else if (isUserJoined) { MatrixClientPeg.get().stopPeeking(); - this._onRoomLoaded(this.state.room); + this.setState({ + unsentMessageError: this._getUnsentMessageError(room), + }); + this._onRoomLoaded(room); } }, @@ -299,10 +280,6 @@ module.exports = React.createClass({ }, componentWillReceiveProps: function(newProps) { - if (newProps.roomAddress != this.props.roomAddress) { - throw new Error("changing room on a RoomView is not supported"); - } - if (newProps.eventId != this.props.eventId) { // when we change focussed event id, hide the search results. this.setState({searchResults: null}); @@ -523,7 +500,7 @@ module.exports = React.createClass({ this._updatePreviewUrlVisibility(room); }, - _warnAboutEncryption: function (room) { + _warnAboutEncryption: function(room) { if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) { return; } @@ -604,20 +581,14 @@ module.exports = React.createClass({ }, onRoom: function(room) { - // This event is fired when the room is 'stored' by the JS SDK, which - // means it's now a fully-fledged room object ready to be used, so - // set it in our state and start using it (ie. init the timeline) - // This will happen if we start off viewing a room we're not joined, - // then join it whilst RoomView is looking at that room. - if (!this.state.room && room.roomId == this._joiningRoomId) { - this._joiningRoomId = undefined; - this.setState({ - room: room, - joining: false, - }); - - this._onRoomLoaded(room); + if (!room || room.roomId !== this.state.roomId) { + return; } + this.setState({ + room: room, + }, () => { + this._onRoomLoaded(room); + }); }, updateTint: function() { @@ -683,7 +654,7 @@ module.exports = React.createClass({ // refresh the tab complete list this.tabComplete.loadEntries(this.state.room); - this._updateAutoComplete(); + this._updateAutoComplete(this.state.room); // if we are now a member of the room, where we were not before, that // means we have finished joining a room we were previously peeking @@ -778,37 +749,43 @@ module.exports = React.createClass({ }, onJoinButtonClicked: function(ev) { - var self = this; - - var cli = MatrixClientPeg.get(); - var mxIdPromise = q(); + const cli = MatrixClientPeg.get(); // If the user is a ROU, allow them to transition to a PWLU if (cli && cli.isGuest()) { + // Join this room once the user has registered and logged in + dis.dispatch({ + action: 'do_after_sync_prepared', + deferred_action: { + action: 'join_room', + room_id: this.state.roomId, + }, + }); + const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); - const defered = q.defer(); - mxIdPromise = defered.promise; const close = Modal.createDialog(SetMxIdDialog, { homeserverUrl: cli.getHomeserverUrl(), onFinished: (submitted, credentials) => { - if (!submitted) { - defered.reject(); - return; + if (submitted) { + this.props.onRegistered(credentials); } - this.props.onRegistered(credentials); - defered.resolve(); }, onDifferentServerClicked: (ev) => { dis.dispatch({action: 'start_registration'}); close(); }, }).close; + return; } - mxIdPromise.then(() => { - this.setState({ - joining: true + q().then(() => { + const signUrl = this.props.thirdPartyInvite ? + this.props.thirdPartyInvite.inviteSignUrl : undefined; + dis.dispatch({ + action: 'join_room', + opts: { inviteSignUrl: signUrl }, }); + // if this is an invite and has the 'direct' hint set, mark it as a DM room now. if (this.state.room) { const me = this.state.room.getMember(MatrixClientPeg.get().credentials.userId); @@ -820,65 +797,8 @@ module.exports = React.createClass({ } } } - return q(); - }).then(() => { - var sign_url = this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : undefined; - return MatrixClientPeg.get().joinRoom(this.props.roomAddress, - { inviteSignUrl: sign_url } ); - }).then(function(resp) { - var roomId = resp.roomId; - - // It is possible that there is no Room yet if state hasn't come down - // from /sync - joinRoom will resolve when the HTTP request to join succeeds, - // NOT when it comes down /sync. If there is no room, we'll keep the - // joining flag set until we see it. - - // We'll need to initialise the timeline when joining, but due to - // the above, we can't do it here: we do it in onRoom instead, - // once we have a useable room object. - var room = MatrixClientPeg.get().getRoom(roomId); - if (!room) { - // wait for the room to turn up in onRoom. - self._joiningRoomId = roomId; - } else { - // we've got a valid room, but that might also just mean that - // it was peekable (so we had one before anyway). If we are - // not yet a member of the room, we will need to wait for that - // to happen, in onRoomStateMember. - var me = MatrixClientPeg.get().credentials.userId; - self.setState({ - joining: !room.hasMembershipState(me, "join"), - room: room - }); - } - }).catch(function(error) { - self.setState({ - joining: false, - joinError: error - }); - - if (!error) return; - - // https://matrix.org/jira/browse/SYN-659 - // Need specific error message if joining a room is refused because the user is a guest and guest access is not allowed - if ( - error.errcode == 'M_GUEST_ACCESS_FORBIDDEN' || - ( - error.errcode == 'M_FORBIDDEN' && - MatrixClientPeg.get().isGuest() - ) - ) { - dis.dispatch({action: 'view_set_mxid'}); - } else { - var msg = error.message ? error.message : JSON.stringify(error); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Failed to join room", - description: msg - }); - } - }).done(); + }); }, onMessageListScroll: function(ev) { @@ -1451,9 +1371,9 @@ module.exports = React.createClass({ } }, - _updateAutoComplete: function() { + _updateAutoComplete: function(room) { const myUserId = MatrixClientPeg.get().credentials.userId; - const members = this.state.room.getJoinedMembers().filter(function(member) { + const members = room.getJoinedMembers().filter(function(member) { if (member.userId !== myUserId) return true; }); UserProvider.getInstance().setUserList(members); @@ -1491,7 +1411,7 @@ module.exports = React.createClass({ // We have no room object for this room, only the ID. // We've got to this room by following a link, possibly a third party invite. - var room_alias = this.props.roomAddress[0] == '#' ? this.props.roomAddress : null; + var room_alias = this.state.room_alias; return (
{ + * this.setState({ cachedPassword: lifecycleStore.getCachedPassword() }) + * }) + * ``` + */ +class LifecycleStore extends Store { + constructor() { + super(dis); + + // Initialise state + this._state = { + deferred_action: null, + }; + } + + _setState(newState) { + this._state = Object.assign(this._state, newState); + this.__emitChange(); + } + + __onDispatch(payload) { + switch (payload.action) { + case 'do_after_sync_prepared': + this._setState({ + deferred_action: payload.deferred_action, + }); + break; + case 'sync_state': + if (payload.state !== 'PREPARED') { + break; + } + console.warn(this._state); + if (!this._state.deferred_action) break; + const deferredAction = Object.assign({}, this._state.deferred_action); + this._setState({ + deferred_action: null, + }); + dis.dispatch(deferredAction); + break; + } + } +} + +let singletonLifecycleStore = null; +if (!singletonLifecycleStore) { + singletonLifecycleStore = new LifecycleStore(); +} +module.exports = singletonLifecycleStore; diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js new file mode 100644 index 0000000000..fe57079859 --- /dev/null +++ b/src/stores/RoomViewStore.js @@ -0,0 +1,145 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import dis from '../dispatcher'; +import {Store} from 'flux/utils'; +import MatrixClientPeg from '../MatrixClientPeg'; + +const INITIAL_STATE = { + // Whether we're joining the currently viewed room + joining: false, + // Any error occurred during joining + joinError: null, + // The room ID of the room + roomId: null, + // The room alias of the room (or null if not originally specified in view_room) + roomAlias: null, + // Whether the current room is loading + roomLoading: false, + // Any error that has occurred during loading + roomLoadError: null, +}; + +/** + * A class for storing application state for RoomView. This is the RoomView's interface +* with a subset of the js-sdk. + * ``` + */ +class RoomViewStore extends Store { + constructor() { + super(dis); + + // Initialise state + this._state = INITIAL_STATE; + } + + _setState(newState) { + this._state = Object.assign(this._state, newState); + this.__emitChange(); + } + + __onDispatch(payload) { + switch (payload.action) { + // view_room: + // - room_alias: '#somealias:matrix.org' + // - room_id: '!roomid123:matrix.org' + case 'view_room': + this._viewRoom(payload); + break; + + // join_room: + // - opts: options for joinRoom + case 'join_room': + this._joinRoom(payload); + break; + } + } + + _viewRoom(payload) { + const address = payload.room_alias || payload.room_id; + if (address[0] == '#') { + this._setState({ + roomLoading: true, + }); + MatrixClientPeg.get().getRoomIdForAlias(address).then( + (result) => { + this._setState({ + roomId: result.room_id, + roomAlias: address, + roomLoading: false, + roomLoadError: null, + }); + }, (err) => { + console.error(err); + this._setState({ + roomLoading: false, + roomLoadError: err, + }); + }); + } else { + this._setState({ + roomId: address, + }); + } + } + + _joinRoom(payload) { + this._setState({ + joining: true, + }); + MatrixClientPeg.get().joinRoom(this._state.roomId, payload.opts).then( + () => { + this._setState({ + joining: false, + }); + }, (err) => { + this._setState({ + joining: false, + joinError: err, + }); + }); + } + + reset() { + this._state = Object.assign({}, INITIAL_STATE); + } + + getRoomId() { + return this._state.roomId; + } + + getRoomAlias() { + return this._state.roomAlias; + } + + isRoomLoading() { + return this._state.roomLoading; + } + + isJoining() { + return this._state.joining; + } + + getJoinError() { + return this._state.joinError; + } + +} + +let singletonRoomViewStore = null; +if (!singletonRoomViewStore) { + singletonRoomViewStore = new RoomViewStore(); +} +module.exports = singletonRoomViewStore; diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index 1570f58688..2fd35ce40a 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -1,3 +1,18 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ import dis from '../dispatcher'; import {Store} from 'flux/utils'; diff --git a/test/components/structures/RoomView-test.js b/test/components/structures/RoomView-test.js deleted file mode 100644 index 8e7c8160b8..0000000000 --- a/test/components/structures/RoomView-test.js +++ /dev/null @@ -1,67 +0,0 @@ -var React = require('react'); -var expect = require('expect'); -var sinon = require('sinon'); -var ReactDOM = require("react-dom"); - -var sdk = require('matrix-react-sdk'); -var RoomView = sdk.getComponent('structures.RoomView'); -var peg = require('../../../src/MatrixClientPeg'); - -var test_utils = require('../../test-utils'); -var q = require('q'); - -var Skinner = require("../../../src/Skinner"); -var stubComponent = require('../../components/stub-component.js'); - -describe('RoomView', function () { - var sandbox; - var parentDiv; - - beforeEach(function() { - test_utils.beforeEach(this); - sandbox = test_utils.stubClient(); - parentDiv = document.createElement('div'); - - this.oldTimelinePanel = Skinner.getComponent('structures.TimelinePanel'); - this.oldRoomHeader = Skinner.getComponent('views.rooms.RoomHeader'); - Skinner.addComponent('structures.TimelinePanel', stubComponent()); - Skinner.addComponent('views.rooms.RoomHeader', stubComponent()); - - peg.get().credentials = { userId: "@test:example.com" }; - }); - - afterEach(function() { - sandbox.restore(); - - ReactDOM.unmountComponentAtNode(parentDiv); - - Skinner.addComponent('structures.TimelinePanel', this.oldTimelinePanel); - Skinner.addComponent('views.rooms.RoomHeader', this.oldRoomHeader); - }); - - it('resolves a room alias to a room id', function (done) { - peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"})); - - function onRoomIdResolved(room_id) { - expect(room_id).toEqual("!randomcharacters:aser.ver"); - done(); - } - - ReactDOM.render(, parentDiv); - }); - - it('joins by alias if given an alias', function (done) { - peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"})); - peg.get().getProfileInfo.returns(q({displayname: "foo"})); - var roomView = ReactDOM.render(, parentDiv); - - peg.get().joinRoom = function(x) { - expect(x).toEqual('#alias:ser.ver'); - done(); - }; - - process.nextTick(function() { - roomView.onJoinButtonClicked(); - }); - }); -}); diff --git a/test/stores/RoomViewStore-test.js b/test/stores/RoomViewStore-test.js new file mode 100644 index 0000000000..7100dced19 --- /dev/null +++ b/test/stores/RoomViewStore-test.js @@ -0,0 +1,56 @@ +import expect from 'expect'; + +import dis from '../../src/dispatcher'; +import RoomViewStore from '../../src/stores/RoomViewStore'; + + +import peg from '../../src/MatrixClientPeg'; + +import * as testUtils from '../test-utils'; +import q from 'q'; + +const dispatch = testUtils.getDispatchForStore(RoomViewStore); + +describe('RoomViewStore', function() { + let sandbox; + + beforeEach(function() { + testUtils.beforeEach(this); + sandbox = testUtils.stubClient(); + peg.get().credentials = { userId: "@test:example.com" }; + + // Reset the state of the store + RoomViewStore.reset(); + }); + + afterEach(function() { + sandbox.restore(); + }); + + it('can be used to view a room by ID and join', function(done) { + peg.get().joinRoom = (roomId) => { + expect(roomId).toBe("!randomcharacters:aser.ver"); + done(); + }; + + dispatch({ action: 'view_room', room_id: '!randomcharacters:aser.ver' }); + dispatch({ action: 'join_room' }); + expect(RoomViewStore.isJoining()).toBe(true); + }); + + it('can be used to view a room by alias and join', function(done) { + peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"})); + peg.get().joinRoom = (roomId) => { + expect(roomId).toBe("!randomcharacters:aser.ver"); + done(); + }; + + dispatch({ action: 'view_room', room_alias: '#somealias2:aser.ver' }); + + // Wait for the next event loop to allow for room alias resolution + setTimeout(() => { + dispatch({ action: 'join_room' }); + expect(RoomViewStore.isJoining()).toBe(true); + }, 0); + }); +}); diff --git a/test/test-utils.js b/test/test-utils.js index 2c866d345c..569208b355 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -4,7 +4,8 @@ import sinon from 'sinon'; import q from 'q'; import ReactTestUtils from 'react-addons-test-utils'; -import peg from '../src/MatrixClientPeg.js'; +import peg from '../src/MatrixClientPeg'; +import dis from '../src/dispatcher'; import jssdk from 'matrix-js-sdk'; const MatrixEvent = jssdk.MatrixEvent; @@ -290,3 +291,13 @@ export function mkStubRoom(roomId = null) { }, }; } + +export function getDispatchForStore(store) { + // Mock the dispatcher by gut-wrenching. Stores can only __emitChange whilst a + // dispatcher `_isDispatching` is true. + return (payload) => { + dis._isDispatching = true; + dis._callbacks[store._dispatchToken](payload); + dis._isDispatching = false; + }; +} From 5f36f797da0b1a195c31b0cf2f45f2d4b8dd5ae6 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 24 May 2017 17:55:36 +0100 Subject: [PATCH 040/416] Implement default welcome page and allow custom URL /w config This changes the default behaviour of displaying the room directory to instead displaying the default homepage. If specified, the config "welcomePageUrl" can be used to override the default '/home.html'. --- src/components/structures/LoggedInView.js | 4 ++-- src/components/structures/MatrixChat.js | 12 ++---------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index e559a21e1a..994e6504cf 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -240,7 +240,8 @@ export default React.createClass({ collapsedRhs={this.props.collapse_rhs} teamServerUrl={this.props.config.teamServerConfig.teamServerURL} teamToken={this.props.teamToken} - /> + homePageUrl={this.props.config.welcomePageUrl} + />; if (!this.props.collapse_rhs) right_panel = break; @@ -276,7 +277,6 @@ export default React.createClass({ selectedRoom={this.props.currentRoomId} collapsed={this.props.collapse_lhs || false} opacity={this.props.sideOpacity} - teamToken={this.props.teamToken} />
{page_element} diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 59ce1b622d..1882831bdc 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -457,10 +457,6 @@ module.exports = React.createClass({ this.notifyNewScreen('directory'); break; case 'view_home_page': - if (!this._teamToken) { - dis.dispatch({action: 'view_room_directory'}); - return; - } this._setPage(PageTypes.HomePage); this.notifyNewScreen('home'); break; @@ -812,7 +808,7 @@ module.exports = React.createClass({ return; } // The user has just logged in after registering - dis.dispatch({action: 'view_room_directory'}); + dis.dispatch({action: 'view_home_page'}); } else { this._showScreenAfterLogin(); } @@ -834,12 +830,8 @@ module.exports = React.createClass({ action: 'view_room', room_id: localStorage.getItem('mx_last_room_id'), }); - } else if (this._teamToken) { - // Team token might be set if we're a guest. - // Guests do not call _onLoggedIn with a teamToken - dis.dispatch({action: 'view_home_page'}); } else { - dis.dispatch({action: 'view_room_directory'}); + dis.dispatch({action: 'view_home_page'}); } }, From dcf2fb68aecd5d1eb64a649eac3bba1912dd96d3 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 24 May 2017 18:02:17 +0100 Subject: [PATCH 041/416] Remove console log --- src/stores/LifecycleStore.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 82a3b1b584..43e2de9d52 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -54,7 +54,6 @@ class LifecycleStore extends Store { if (payload.state !== 'PREPARED') { break; } - console.warn(this._state); if (!this._state.deferred_action) break; const deferredAction = Object.assign({}, this._state.deferred_action); this._setState({ From fffe425730688b1d1adea59c813bdc6b6b695273 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 24 May 2017 18:04:04 +0100 Subject: [PATCH 042/416] Add non-null RoomView key --- src/components/structures/LoggedInView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index df24fbb33b..5022b983f0 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -196,7 +196,7 @@ export default React.createClass({ oobData={this.props.roomOobData} highlightedEventId={this.props.highlightedEventId} eventPixelOffset={this.props.initialEventPixelOffset} - key={this.props.currentRoomId} + key={this.props.currentRoomId || 'roomview'} opacity={this.props.middleOpacity} collapsedRhs={this.props.collapse_rhs} ConferenceHandler={this.props.ConferenceHandler} From 09d0ab7df5f9e6602ca2e8e705e4d59a1304f94e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 May 2017 01:01:40 +0100 Subject: [PATCH 043/416] attempt to make media selector work everywhere (TM) loadDevices not only in electron Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/CallMediaHandler.js | 2 + src/components/structures/LoggedInView.js | 4 +- src/components/structures/UserSettings.js | 52 ++++++++++++++++------- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 9133a6548d..4f82e003b9 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -26,6 +26,8 @@ export default { const audioIn = {}; const videoIn = {}; + if (devices.some((device) => !device.label)) return false; + devices.forEach((device) => { switch (device.kind) { case 'audioinput': audioIn[device.deviceId] = device.label; break; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index a84661bcd2..1a7b7e06e4 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -72,9 +72,7 @@ export default React.createClass({ // RoomView.getScrollState() this._scrollStateMap = {}; - // Only run these in electron, at least until a better mechanism for perms exists - // https://w3c.github.io/permissions/#dom-permissionname-device-info - if (window && window.process && window.process.type) CallMediaHandler.loadDevices(); + CallMediaHandler.loadDevices(); document.addEventListener('keydown', this._onKeyDown); }, diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index de88566300..58c6bb7c20 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -178,17 +178,7 @@ module.exports = React.createClass({ }); } - q().then(() => { - return CallMediaHandler.getDevices(); - }).then((mediaDevices) => { - console.log("got mediaDevices", mediaDevices, this._unmounted); - if (this._unmounted) return; - this.setState({ - mediaDevices, - activeAudioInput: this._localSettings['webrtc_audioinput'] || 'default', - activeVideoInput: this._localSettings['webrtc_videoinput'] || 'default', - }); - }); + this._refreshMediaDevices(); // Bulk rejecting invites: // /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms() @@ -210,8 +200,6 @@ module.exports = React.createClass({ this._syncedSettings = syncedSettings; this._localSettings = UserSettingsStore.getLocalSettings(); - this._setAudioInput = this._setAudioInput.bind(this); - this._setVideoInput = this._setVideoInput.bind(this); }, componentDidMount: function() { @@ -233,6 +221,20 @@ module.exports = React.createClass({ } }, + _refreshMediaDevices: function() { + q().then(() => { + return CallMediaHandler.getDevices(); + }).then((mediaDevices) => { + // console.log("got mediaDevices", mediaDevices, this._unmounted); + if (this._unmounted) return; + this.setState({ + mediaDevices, + activeAudioInput: this._localSettings['webrtc_audioinput'] || 'default', + activeVideoInput: this._localSettings['webrtc_videoinput'] || 'default', + }); + }); + }, + _refreshFromServer: function() { const self = this; q.all([ @@ -818,9 +820,29 @@ module.exports = React.createClass({ CallMediaHandler.setVideoInput(deviceId); }, + _requestMediaPermissions: function() { + console.log("Request media perms"); + const getUserMedia = ( + window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia + ); + if (getUserMedia) { + return getUserMedia.apply(window.navigator, [ + { video: true, audio: true }, + this._refreshMediaDevices, + function() {}, + ]); + } + }, + _renderWebRtcSettings: function() { - if (!(window && window.process && window.process.type) - || !this.state.mediaDevices) return; + if (this.state.mediaDevices === false) { + return
+

WebRTC

+
+
Missing Media Permissions, click to request.
+
+
; + } else if (!this.state.mediaDevices) return; const Dropdown = sdk.getComponent('elements.Dropdown'); From 8158ec6d54c062c6a3c49b92ee5c1fe1e64e969b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 May 2017 01:25:17 +0100 Subject: [PATCH 044/416] touchups Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 58c6bb7c20..0d182b27ab 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -820,8 +820,7 @@ module.exports = React.createClass({ CallMediaHandler.setVideoInput(deviceId); }, - _requestMediaPermissions: function() { - console.log("Request media perms"); + _requestMediaPermissions: function(event) { const getUserMedia = ( window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia ); @@ -829,7 +828,13 @@ module.exports = React.createClass({ return getUserMedia.apply(window.navigator, [ { video: true, audio: true }, this._refreshMediaDevices, - function() {}, + function() { + const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); + Modal.createDialog(ErrorDialog, { + title: "No media permissions", + description: "You may need to manually permit Riot to access your microphone/webcam", + }); + }, ]); } }, @@ -839,15 +844,17 @@ module.exports = React.createClass({ return

WebRTC

-
Missing Media Permissions, click to request.
+

+ Missing Media Permissions, click here to request. +

; } else if (!this.state.mediaDevices) return; const Dropdown = sdk.getComponent('elements.Dropdown'); - let microphoneDropdown =
No Microphones detected
; - let webcamDropdown =
No Webcams detected
; + let microphoneDropdown =

No Microphones detected

; + let webcamDropdown =

No Webcams detected

; const audioInputs = this.state.mediaDevices.audioinput; if ('default' in audioInputs) { From 8fc44a9b6661a6a5dab303d6895e903a10d2aed1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 09:30:57 +0100 Subject: [PATCH 045/416] Add comment to explain sync_state dispatch --- src/components/structures/MatrixChat.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b1814bc322..dca73a4601 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -895,6 +895,11 @@ module.exports = React.createClass({ }); cli.on('sync', function(state, prevState) { + // LifecycleStore and others cannot directly subscribe to matrix client for + // events because flux only allows store state changes during flux dispatches. + // So dispatch directly from here. Ideally we'd use a SyncStateStore that + // would do this dispatch and expose the sync state itself (by listening to + // its own dispatch). dis.dispatch({action: 'sync_state', prevState, state}); self.updateStatusIndicator(state, prevState); if (state === "SYNCING" && prevState === "SYNCING") { From c894c83fbe2eaced0c2313e405008c62e9484914 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 11:02:48 +0100 Subject: [PATCH 046/416] Remove GuestWarningBar --- src/components/structures/LoggedInView.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index d2ae57cda4..4687afbee1 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -178,7 +178,6 @@ export default React.createClass({ const RoomDirectory = sdk.getComponent('structures.RoomDirectory'); const HomePage = sdk.getComponent('structures.HomePage'); const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); - const GuestWarningBar = sdk.getComponent('globals.GuestWarningBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar'); @@ -253,8 +252,6 @@ export default React.createClass({ topBar = ; - } else if (this.props.matrixClient.isGuest()) { - topBar = ; } else if (this.state.userHasGeneratedPassword) { topBar = ; } else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) { From 5531f274354df79980152d05521a23114cb5d25b Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Apr 2017 18:59:16 +0100 Subject: [PATCH 047/416] Make the left panel more friendly to new users https://github.com/vector-im/riot-web/issues/3609 Conflicts: src/components/views/rooms/RoomList.js cherry-picking commit f5f35e3. --- src/components/views/rooms/RoomList.js | 163 +++++++++++++++++++------ 1 file changed, 124 insertions(+), 39 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 760b0543c6..9dfa99fb44 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -29,7 +29,14 @@ var Rooms = require('../../../Rooms'); import DMRoomMap from '../../../utils/DMRoomMap'; var Receipt = require('../../../utils/Receipt'); -var HIDE_CONFERENCE_CHANS = true; +const HIDE_CONFERENCE_CHANS = true; + +const VERBS = { + 'm.favourite': 'favourite', + 'im.vector.fake.direct': 'tag direct chat', + 'im.vector.fake.recent': 'restore', + 'm.lowpriority': 'demote', +}; module.exports = React.createClass({ displayName: 'RoomList', @@ -44,6 +51,7 @@ module.exports = React.createClass({ getInitialState: function() { return { isLoadingLeftRooms: false, + totalRoomCount: null, lists: {}, incomingCall: null, }; @@ -63,8 +71,17 @@ module.exports = React.createClass({ cli.on("RoomMember.name", this.onRoomMemberName); cli.on("accountData", this.onAccountData); - var s = this.getRoomLists(); - this.setState(s); + // lookup for which lists a given roomId is currently in. + this.listsForRoomId = {}; + + this.refreshRoomList(); + + // order of the sublists + //this.listOrder = []; + + // loop count to stop a stack overflow if the user keeps waggling the + // mouse for >30s in a row, or if running under mocha + this._delayedRefreshRoomListLoopCount = 0 }, componentDidMount: function() { @@ -202,31 +219,33 @@ module.exports = React.createClass({ }, 500), refreshRoomList: function() { - // console.log("DEBUG: Refresh room list delta=%s ms", - // (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs)) - // ); - - // TODO: rather than bluntly regenerating and re-sorting everything - // every time we see any kind of room change from the JS SDK - // we could do incremental updates on our copy of the state - // based on the room which has actually changed. This would stop - // us re-rendering all the sublists every time anything changes anywhere - // in the state of the client. - this.setState(this.getRoomLists()); + // TODO: ideally we'd calculate this once at start, and then maintain + // any changes to it incrementally, updating the appropriate sublists + // as needed. + // Alternatively we'd do something magical with Immutable.js or similar. + const lists = this.getRoomLists(); + let totalRooms = 0; + for (const l of Object.values(lists)) { + totalRooms += l.length; + } + this.setState({ + lists: this.getRoomLists(), + totalRoomCount: totalRooms, + }); // this._lastRefreshRoomListTs = Date.now(); }, getRoomLists: function() { var self = this; - var s = { lists: {} }; + const lists = {}; - s.lists["im.vector.fake.invite"] = []; - s.lists["m.favourite"] = []; - s.lists["im.vector.fake.recent"] = []; - s.lists["im.vector.fake.direct"] = []; - s.lists["m.lowpriority"] = []; - s.lists["im.vector.fake.archived"] = []; + lists["im.vector.fake.invite"] = []; + lists["m.favourite"] = []; + lists["im.vector.fake.recent"] = []; + lists["im.vector.fake.direct"] = []; + lists["m.lowpriority"] = []; + lists["im.vector.fake.archived"] = []; const dmRoomMap = new DMRoomMap(MatrixClientPeg.get()); @@ -240,7 +259,8 @@ module.exports = React.createClass({ // ", prevMembership = " + me.events.member.getPrevContent().membership); if (me.membership == "invite") { - s.lists["im.vector.fake.invite"].push(room); + self.listsForRoomId[room.roomId].push("im.vector.fake.invite"); + lists["im.vector.fake.invite"].push(room); } else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) { // skip past this room & don't put it in any lists @@ -254,48 +274,55 @@ module.exports = React.createClass({ if (tagNames.length) { for (var i = 0; i < tagNames.length; i++) { var tagName = tagNames[i]; - s.lists[tagName] = s.lists[tagName] || []; - s.lists[tagNames[i]].push(room); + lists[tagName] = lists[tagName] || []; + lists[tagName].push(room); + self.listsForRoomId[room.roomId].push(tagName); + otherTagNames[tagName] = 1; } } else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { // "Direct Message" rooms (that we're still in and that aren't otherwise tagged) - s.lists["im.vector.fake.direct"].push(room); + self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); + lists["im.vector.fake.direct"].push(room); } else { - s.lists["im.vector.fake.recent"].push(room); + self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); + lists["im.vector.fake.recent"].push(room); } } else if (me.membership === "leave") { - s.lists["im.vector.fake.archived"].push(room); + self.listsForRoomId[room.roomId].push("im.vector.fake.archived"); + lists["im.vector.fake.archived"].push(room); } else { console.error("unrecognised membership: " + me.membership + " - this should never happen"); } }); - if (s.lists["im.vector.fake.direct"].length == 0 && + if (lists["im.vector.fake.direct"].length == 0 && MatrixClientPeg.get().getAccountData('m.direct') === undefined && !MatrixClientPeg.get().isGuest()) { // scan through the 'recents' list for any rooms which look like DM rooms // and make them DM rooms - const oldRecents = s.lists["im.vector.fake.recent"]; - s.lists["im.vector.fake.recent"] = []; + const oldRecents = lists["im.vector.fake.recent"]; + lists["im.vector.fake.recent"] = []; for (const room of oldRecents) { const me = room.getMember(MatrixClientPeg.get().credentials.userId); if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { - s.lists["im.vector.fake.direct"].push(room); + self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); + lists["im.vector.fake.direct"].push(room); } else { - s.lists["im.vector.fake.recent"].push(room); + self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); + lists["im.vector.fake.recent"].push(room); } } // save these new guessed DM rooms into the account data const newMDirectEvent = {}; - for (const room of s.lists["im.vector.fake.direct"]) { + for (const room of lists["im.vector.fake.direct"]) { const me = room.getMember(MatrixClientPeg.get().credentials.userId); const otherPerson = Rooms.getOnlyOtherMember(room, me); if (!otherPerson) continue; @@ -313,7 +340,22 @@ module.exports = React.createClass({ // we actually apply the sorting to this when receiving the prop in RoomSubLists. - return s; + // we'll need this when we get to iterating through lists programatically - e.g. ctrl-shift-up/down +/* + this.listOrder = [ + "im.vector.fake.invite", + "m.favourite", + "im.vector.fake.recent", + "im.vector.fake.direct", + Object.keys(otherTagNames).filter(tagName=>{ + return (!tagName.match(/^m\.(favourite|lowpriority)$/)); + }).sort(), + "m.lowpriority", + "im.vector.fake.archived" + ]; +*/ + + return lists; }, _getScrollNode: function() { @@ -467,6 +509,49 @@ module.exports = React.createClass({ this.refs.gemscroll.forceUpdate(); }, + _getEmptyContent: function(section) { + let greyed = false; + if (this.state.totalRoomCount === 0) { + const TintableSvg = sdk.getComponent('elements.TintableSvg'); + switch (section) { + case 'm.favourite': + case 'm.lowpriority': + greyed = true; + break; + case 'im.vector.fake.direct': + return
+
+ +
+ Use the button below to chat with someone! +
; + case 'im.vector.fake.recent': + return
+
+ +
+ Use the button below to browse the room directory +

+
+ +
+ or this button to start a new one! +
; + } + } + const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); + + const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section); + + let label; + if (greyed) { + label = {labelText}; + } else { + label = labelText; + } + return ; + }, + render: function() { var RoomSubList = sdk.getComponent('structures.RoomSubList'); var self = this; @@ -489,7 +574,7 @@ module.exports = React.createClass({ Date: Fri, 28 Apr 2017 11:20:29 +0100 Subject: [PATCH 048/416] Other empty sections no longer need to be greyed --- src/components/views/rooms/RoomList.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 9dfa99fb44..e285c1841e 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -510,14 +510,9 @@ module.exports = React.createClass({ }, _getEmptyContent: function(section) { - let greyed = false; if (this.state.totalRoomCount === 0) { const TintableSvg = sdk.getComponent('elements.TintableSvg'); switch (section) { - case 'm.favourite': - case 'm.lowpriority': - greyed = true; - break; case 'im.vector.fake.direct': return
@@ -543,13 +538,7 @@ module.exports = React.createClass({ const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section); - let label; - if (greyed) { - label = {labelText}; - } else { - label = labelText; - } - return ; + return ; }, render: function() { From bff0577cb61bfb8095c23254f618d9bb9a42a131 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 May 2017 13:55:52 +0100 Subject: [PATCH 049/416] Add buttons to room sub list headers Conflicts: src/component-index.js src/components/views/rooms/RoomList.js cherry-picking commit ce119a6. --- src/components/views/elements/RoleButton.js | 75 +++++++++++++++++++++ src/components/views/rooms/RoomList.js | 40 +++++++---- 2 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/components/views/elements/RoleButton.js diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/RoleButton.js new file mode 100644 index 0000000000..06006a5779 --- /dev/null +++ b/src/components/views/elements/RoleButton.js @@ -0,0 +1,75 @@ +/* +Copyright Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import AccessibleButton from './AccessibleButton'; +import dis from '../../../dispatcher'; +import sdk from '../../../index'; + +export default React.createClass({ + displayName: 'RoleButton', + + propTypes: { + role: PropTypes.string.isRequired, + size: PropTypes.string, + }, + + getDefaultProps: function() { + return { + size: 25, + }; + }, + + _onClick: function(ev) { + ev.stopPropagation(); + + let action; + switch(this.props.role) { + case 'start_chat': + action = 'view_create_chat'; + break; + case 'room_directory': + action = 'view_room_directory'; + break; + case 'create_room': + action = 'view_create_room'; + break; + } + if (action) dis.dispatch({action: action}); + }, + + _getIconPath() { + switch(this.props.role) { + case 'start_chat': + return 'img/icons-people.svg'; + case 'room_directory': + return 'img/icons-directory.svg'; + case 'create_room': + return 'img/icons-create-room.svg'; + } + }, + + render: function() { + const TintableSvg = sdk.getComponent("elements.TintableSvg"); + + return ( + + + + ); + } +}); diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index e285c1841e..9a64c16239 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -510,27 +511,23 @@ module.exports = React.createClass({ }, _getEmptyContent: function(section) { + const RoleButton = sdk.getComponent('elements.RoleButton'); if (this.state.totalRoomCount === 0) { const TintableSvg = sdk.getComponent('elements.TintableSvg'); switch (section) { case 'im.vector.fake.direct': return
-
- -
- Use the button below to chat with someone! + Press + + to start a chat with someone
; case 'im.vector.fake.recent': return
-
- -
- Use the button below to browse the room directory -

-
- -
- or this button to start a new one! + You're not in any rooms yet! Press + + to make a room or + + to browse the directory
; } } @@ -541,6 +538,21 @@ module.exports = React.createClass({ return ; }, + _getHeaderItems: function(section) { + const RoleButton = sdk.getComponent('elements.RoleButton'); + switch (section) { + case 'im.vector.fake.direct': + return + + ; + case 'im.vector.fake.recent': + return + + + ; + } + }, + render: function() { var RoomSubList = sdk.getComponent('structures.RoomSubList'); var self = this; @@ -577,6 +589,7 @@ module.exports = React.createClass({ label="People" tagName="im.vector.fake.direct" emptyContent={this._getEmptyContent('im.vector.fake.direct')} + headerItems={this._getHeaderItems('im.vector.fake.direct')} editable={ true } order="recent" selectedRoom={ self.props.selectedRoom } @@ -591,6 +604,7 @@ module.exports = React.createClass({ label="Rooms" editable={ true } emptyContent={this._getEmptyContent('im.vector.fake.recent')} + headerItems={this._getHeaderItems('im.vector.fake.recent')} order="recent" selectedRoom={ self.props.selectedRoom } incomingCall={ self.state.incomingCall } From 54af06e8e12bfe86f11b4a3131ea155b8e161f3c Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 May 2017 15:02:21 +0100 Subject: [PATCH 050/416] What year is it? Who's the president? --- src/components/views/elements/RoleButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/RoleButton.js index 06006a5779..f20b4c6b88 100644 --- a/src/components/views/elements/RoleButton.js +++ b/src/components/views/elements/RoleButton.js @@ -1,5 +1,5 @@ /* -Copyright Vector Creations Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From a996f52ea34c0d4c7cc072c7cf068baf3b9cde1b Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 May 2017 15:38:09 +0100 Subject: [PATCH 051/416] Make bottom left menu buttons use RoleButton too --- src/components/views/elements/RoleButton.js | 53 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/RoleButton.js index f20b4c6b88..60f227a067 100644 --- a/src/components/views/elements/RoleButton.js +++ b/src/components/views/elements/RoleButton.js @@ -31,6 +31,13 @@ export default React.createClass({ getDefaultProps: function() { return { size: 25, + tooltip: false, + }; + }, + + getInitialState: function() { + return { + showTooltip: false, }; }, @@ -48,10 +55,39 @@ export default React.createClass({ case 'create_room': action = 'view_create_room'; break; + case 'home_page': + action = 'view_home_page'; + break; + case 'settings': + action = 'view_user_settings'; + break; } if (action) dis.dispatch({action: action}); }, + _onMouseEnter: function() { + if (this.props.tooltip) this.setState({showTooltip: true}); + }, + + _onMouseLeave: function() { + this.setState({showTooltip: false}); + }, + + _getLabel() { + switch(this.props.role) { + case 'start_chat': + return 'Start chat'; + case 'room_directory': + return 'Room directory'; + case 'create_room': + return 'Create new room'; + case 'home_page': + return 'Welcome page'; + case 'settings': + return 'Settings'; + } + }, + _getIconPath() { switch(this.props.role) { case 'start_chat': @@ -60,15 +96,30 @@ export default React.createClass({ return 'img/icons-directory.svg'; case 'create_room': return 'img/icons-create-room.svg'; + case 'home_page': + return 'img/icons-home.svg'; + case 'settings': + return 'img/icons-settings.svg'; } }, render: function() { const TintableSvg = sdk.getComponent("elements.TintableSvg"); + let tooltip; + if (this.state.showTooltip) { + const RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); + tooltip = ; + } + return ( - + + {tooltip} ); } From 3d3d89202e01100f1162d30542b59c8826c19e29 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 May 2017 15:46:24 +0100 Subject: [PATCH 052/416] Year --- src/components/views/rooms/RoomList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 9a64c16239..ecb178d145 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -1,6 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd -Copyright Vector Creations Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 99efbbee5e23913221d95bc9d8457bed24a4e0ae Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 May 2017 16:22:06 +0100 Subject: [PATCH 053/416] Depend on prop-types module So we can start writing code compatible with new React --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 059fdd390f..572dcddeb5 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "lodash": "^4.13.1", "matrix-js-sdk": "0.7.8", "optimist": "^0.6.1", + "prop-types": "^15.5.8", "q": "^1.4.1", "react": "^15.4.0", "react-addons-css-transition-group": "15.3.2", From dc2274df54896b48f836854cf46cd10b525d41c8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 May 2017 18:08:04 +0100 Subject: [PATCH 054/416] Hide empty tips if collapsed --- src/components/views/rooms/RoomList.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index ecb178d145..2dce02cc78 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -511,6 +511,12 @@ module.exports = React.createClass({ }, _getEmptyContent: function(section) { + const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); + + if (this.props.collapsed) { + return ; + } + const RoleButton = sdk.getComponent('elements.RoleButton'); if (this.state.totalRoomCount === 0) { const TintableSvg = sdk.getComponent('elements.TintableSvg'); @@ -531,7 +537,6 @@ module.exports = React.createClass({
; } } - const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section); From 9337158a470e2d23d16cce0054a931e5f97a3b0d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 May 2017 14:25:18 +0100 Subject: [PATCH 055/416] Separate classes for the different buttons Also rename RoleButton to ActionButton because it's not being given a Role any more. Conflicts: src/component-index.js cherry-picking commit 4a5821e. --- .../{RoleButton.js => ActionButton.js} | 60 +++---------------- .../views/elements/CreateRoomButton.js | 37 ++++++++++++ src/components/views/elements/HomeButton.js | 37 ++++++++++++ .../views/elements/RoomDirectoryButton.js | 37 ++++++++++++ .../views/elements/SettingsButton.js | 37 ++++++++++++ .../views/elements/StartChatButton.js | 37 ++++++++++++ src/components/views/rooms/RoomList.js | 20 ++++--- 7 files changed, 204 insertions(+), 61 deletions(-) rename src/components/views/elements/{RoleButton.js => ActionButton.js} (54%) create mode 100644 src/components/views/elements/CreateRoomButton.js create mode 100644 src/components/views/elements/HomeButton.js create mode 100644 src/components/views/elements/RoomDirectoryButton.js create mode 100644 src/components/views/elements/SettingsButton.js create mode 100644 src/components/views/elements/StartChatButton.js diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/ActionButton.js similarity index 54% rename from src/components/views/elements/RoleButton.js rename to src/components/views/elements/ActionButton.js index 60f227a067..6d6289ddab 100644 --- a/src/components/views/elements/RoleButton.js +++ b/src/components/views/elements/ActionButton.js @@ -24,8 +24,11 @@ export default React.createClass({ displayName: 'RoleButton', propTypes: { - role: PropTypes.string.isRequired, size: PropTypes.string, + tooltip: PropTypes.bool, + action: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + iconPath: PropTypes.string.isRequired, }, getDefaultProps: function() { @@ -43,26 +46,7 @@ export default React.createClass({ _onClick: function(ev) { ev.stopPropagation(); - - let action; - switch(this.props.role) { - case 'start_chat': - action = 'view_create_chat'; - break; - case 'room_directory': - action = 'view_room_directory'; - break; - case 'create_room': - action = 'view_create_room'; - break; - case 'home_page': - action = 'view_home_page'; - break; - case 'settings': - action = 'view_user_settings'; - break; - } - if (action) dis.dispatch({action: action}); + dis.dispatch({action: this.props.action}); }, _onMouseEnter: function() { @@ -73,43 +57,13 @@ export default React.createClass({ this.setState({showTooltip: false}); }, - _getLabel() { - switch(this.props.role) { - case 'start_chat': - return 'Start chat'; - case 'room_directory': - return 'Room directory'; - case 'create_room': - return 'Create new room'; - case 'home_page': - return 'Welcome page'; - case 'settings': - return 'Settings'; - } - }, - - _getIconPath() { - switch(this.props.role) { - case 'start_chat': - return 'img/icons-people.svg'; - case 'room_directory': - return 'img/icons-directory.svg'; - case 'create_room': - return 'img/icons-create-room.svg'; - case 'home_page': - return 'img/icons-home.svg'; - case 'settings': - return 'img/icons-settings.svg'; - } - }, - render: function() { const TintableSvg = sdk.getComponent("elements.TintableSvg"); let tooltip; if (this.state.showTooltip) { const RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); - tooltip = ; + tooltip = ; } return ( @@ -118,7 +72,7 @@ export default React.createClass({ onMouseEnter={this._onMouseEnter} onMouseLeave={this._onMouseLeave} > - + {tooltip} ); diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js new file mode 100644 index 0000000000..d6b6526d6c --- /dev/null +++ b/src/components/views/elements/CreateRoomButton.js @@ -0,0 +1,37 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import sdk from '../../../index'; +import PropTypes from 'prop-types'; + +const CreateRoomButton = function(props) { + const ActionButton = sdk.getComponent('elements.ActionButton'); + return ( + + ); +}; + +CreateRoomButton.propTypes = { + size: PropTypes.string, + tooltip: PropTypes.bool, +}; + +export default CreateRoomButton; diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js new file mode 100644 index 0000000000..4c7f295c87 --- /dev/null +++ b/src/components/views/elements/HomeButton.js @@ -0,0 +1,37 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import sdk from '../../../index'; +import PropTypes from 'prop-types'; + +const HomeButton = function(props) { + const ActionButton = sdk.getComponent('elements.ActionButton'); + return ( + + ); +}; + +HomeButton.propTypes = { + size: PropTypes.string, + tooltip: PropTypes.bool, +}; + +export default HomeButton; diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js new file mode 100644 index 0000000000..651dd8edd0 --- /dev/null +++ b/src/components/views/elements/RoomDirectoryButton.js @@ -0,0 +1,37 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import sdk from '../../../index'; +import PropTypes from 'prop-types'; + +const RoomDirectoryButton = function(props) { + const ActionButton = sdk.getComponent('elements.ActionButton'); + return ( + + ); +}; + +RoomDirectoryButton.propTypes = { + size: PropTypes.string, + tooltip: PropTypes.bool, +}; + +export default RoomDirectoryButton; diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js new file mode 100644 index 0000000000..51da6e3fd1 --- /dev/null +++ b/src/components/views/elements/SettingsButton.js @@ -0,0 +1,37 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import sdk from '../../../index'; +import PropTypes from 'prop-types'; + +const SettingsButton = function(props) { + const ActionButton = sdk.getComponent('elements.ActionButton'); + return ( + + ); +}; + +SettingsButton.propTypes = { + size: PropTypes.string, + tooltip: PropTypes.bool, +}; + +export default SettingsButton; diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js new file mode 100644 index 0000000000..66cd911754 --- /dev/null +++ b/src/components/views/elements/StartChatButton.js @@ -0,0 +1,37 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import sdk from '../../../index'; +import PropTypes from 'prop-types'; + +const StartChatButton = function(props) { + const ActionButton = sdk.getComponent('elements.ActionButton'); + return ( + + ); +}; + +StartChatButton.propTypes = { + size: PropTypes.string, + tooltip: PropTypes.bool, +}; + +export default StartChatButton; diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 2dce02cc78..8c8fd3ea86 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -517,22 +517,24 @@ module.exports = React.createClass({ return ; } - const RoleButton = sdk.getComponent('elements.RoleButton'); + const StartChatButton = sdk.getComponent('elements.StartChatButton'); + const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); + const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); if (this.state.totalRoomCount === 0) { const TintableSvg = sdk.getComponent('elements.TintableSvg'); switch (section) { case 'im.vector.fake.direct': return
Press - + to start a chat with someone
; case 'im.vector.fake.recent': return
You're not in any rooms yet! Press - + to make a room or - + to browse the directory
; } @@ -544,16 +546,18 @@ module.exports = React.createClass({ }, _getHeaderItems: function(section) { - const RoleButton = sdk.getComponent('elements.RoleButton'); + const StartChatButton = sdk.getComponent('elements.StartChatButton'); + const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); + const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); switch (section) { case 'im.vector.fake.direct': return - + ; case 'im.vector.fake.recent': return - - + + ; } }, From 5e855e6fee3c5cf068263967baa6d0c544c2a32c Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 May 2017 14:56:26 +0100 Subject: [PATCH 056/416] Size is a string, import react React gets put in by the JSX transpile --- src/components/views/elements/ActionButton.js | 2 +- src/components/views/elements/CreateRoomButton.js | 1 + src/components/views/elements/HomeButton.js | 1 + src/components/views/elements/RoomDirectoryButton.js | 1 + src/components/views/elements/SettingsButton.js | 1 + src/components/views/elements/StartChatButton.js | 1 + 6 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js index 6d6289ddab..267388daf6 100644 --- a/src/components/views/elements/ActionButton.js +++ b/src/components/views/elements/ActionButton.js @@ -33,7 +33,7 @@ export default React.createClass({ getDefaultProps: function() { return { - size: 25, + size: "25", tooltip: false, }; }, diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js index d6b6526d6c..e7e526d36b 100644 --- a/src/components/views/elements/CreateRoomButton.js +++ b/src/components/views/elements/CreateRoomButton.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from 'react'; import sdk from '../../../index'; import PropTypes from 'prop-types'; diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js index 4c7f295c87..5c446f24c9 100644 --- a/src/components/views/elements/HomeButton.js +++ b/src/components/views/elements/HomeButton.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from 'react'; import sdk from '../../../index'; import PropTypes from 'prop-types'; diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js index 651dd8edd0..5e68776a15 100644 --- a/src/components/views/elements/RoomDirectoryButton.js +++ b/src/components/views/elements/RoomDirectoryButton.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from 'react'; import sdk from '../../../index'; import PropTypes from 'prop-types'; diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js index 51da6e3fd1..c6438da277 100644 --- a/src/components/views/elements/SettingsButton.js +++ b/src/components/views/elements/SettingsButton.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from 'react'; import sdk from '../../../index'; import PropTypes from 'prop-types'; diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js index 66cd911754..02d5677a7c 100644 --- a/src/components/views/elements/StartChatButton.js +++ b/src/components/views/elements/StartChatButton.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from 'react'; import sdk from '../../../index'; import PropTypes from 'prop-types'; From 548f319816d94955fc45fc68f51da5f6dcc2787e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 5 May 2017 17:51:14 +0100 Subject: [PATCH 057/416] Remove redundant role elements --- src/components/views/rooms/RoomList.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 8c8fd3ea86..cde2bec7da 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -552,12 +552,12 @@ module.exports = React.createClass({ switch (section) { case 'im.vector.fake.direct': return - + ; case 'im.vector.fake.recent': return - - + + ; } }, From 3185d3ed41d376d52d65583247a08a74a12f1983 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 13:54:59 +0100 Subject: [PATCH 058/416] Re-add bouncing/callout animation to action buttons --- src/components/views/elements/ActionButton.js | 4 ++++ src/components/views/elements/CreateRoomButton.js | 1 + src/components/views/elements/RoomDirectoryButton.js | 1 + src/components/views/elements/StartChatButton.js | 3 ++- src/components/views/rooms/RoomList.js | 6 +++--- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js index 267388daf6..08fb6faa1d 100644 --- a/src/components/views/elements/ActionButton.js +++ b/src/components/views/elements/ActionButton.js @@ -27,6 +27,7 @@ export default React.createClass({ size: PropTypes.string, tooltip: PropTypes.bool, action: PropTypes.string.isRequired, + mouseOverAction: PropTypes.string, label: PropTypes.string.isRequired, iconPath: PropTypes.string.isRequired, }, @@ -51,6 +52,9 @@ export default React.createClass({ _onMouseEnter: function() { if (this.props.tooltip) this.setState({showTooltip: true}); + if (this.props.mouseOverAction) { + dis.dispatch({action: this.props.mouseOverAction}); + } }, _onMouseLeave: function() { diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js index e7e526d36b..82643559b3 100644 --- a/src/components/views/elements/CreateRoomButton.js +++ b/src/components/views/elements/CreateRoomButton.js @@ -22,6 +22,7 @@ const CreateRoomButton = function(props) { const ActionButton = sdk.getComponent('elements.ActionButton'); return ( Press - + to start a chat with someone
; case 'im.vector.fake.recent': return
You're not in any rooms yet! Press - + to make a room or - + to browse the directory
; } From 7900bf1c7de0d170b3ca055332d4bebb3f10f4ef Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 13:55:37 +0100 Subject: [PATCH 059/416] Don't show "Drop to ..." if total rooms = 0 --- src/components/views/rooms/RoomList.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 98a5229d6a..216dd972cf 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -540,6 +540,10 @@ module.exports = React.createClass({ } } + if (this.state.totalRoomCount === 0) { + return null; + } + const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section); return ; From 51c8ee6db23f5eacaa348a58324df69704b06a15 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 14:38:12 +0100 Subject: [PATCH 060/416] Allow teamServerConfig to be missing --- src/components/structures/LoggedInView.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 4687afbee1..8cd2bf8a71 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -232,9 +232,15 @@ export default React.createClass({ break; case PageTypes.HomePage: + // If team server config is present, pass the teamServerURL. props.teamToken + // must also be set for the team page to be displayed, otherwise the + // welcomePageUrl is used (which might be undefined). + const teamServerUrl = this.props.config.teamServerConfig ? + this.props.config.teamServerConfig.teamServerURL : null; + page_element = ; From 2265b59287b44c0ad8d473c98e59a84209141df2 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 14:54:28 +0100 Subject: [PATCH 061/416] Remove warm-fuzzy after setting mxid --- src/components/views/dialogs/SetMxIdDialog.js | 39 +------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index c371fdd35a..86b5fccbc2 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -53,9 +53,6 @@ export default React.createClass({ doingUIAuth: false, // Indicate error with auth authError: '', - - // Indicate success of setting mxid - success: false, }; }, @@ -98,10 +95,6 @@ export default React.createClass({ }); }, - onSuccessContinue: function() { - this.props.onFinished(true, this._registeredCreds); - }, - _doUsernameCheck: function() { // Check if username is available return this._matrixClient.isUsernameAvailable(this.state.username).then( @@ -169,7 +162,7 @@ export default React.createClass({ // XXX Implement RTS /register here const teamToken = null; - this._registeredCreds = { + this.props.onFinished(true, { userId: response.user_id, deviceId: response.device_id, homeserverUrl: this._matrixClient.getHomeserverUrl(), @@ -177,11 +170,6 @@ export default React.createClass({ accessToken: response.access_token, password: this._generatedPassword, teamToken: teamToken, - }; - - // Before continuing, show a warm-fuzzy success and only submit onSuccessContinue - this.setState({ - success: true, }); }, @@ -231,31 +219,6 @@ export default React.createClass({ !this.state.usernameError && !this.state.usernameBusy; - if (this.state.success) { - // XXX BaseDialog needs an onFinished - return ( - -
-

- You have successfully - picked { this.state.username } as your - username and you now have access to the full - set of features on Riot. -

-
-
- -
-
- ); - } - return ( Date: Thu, 25 May 2017 15:20:02 +0100 Subject: [PATCH 062/416] Unbreak the roomlist --- src/components/views/rooms/RoomList.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 216dd972cf..efadda08ac 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -72,9 +72,6 @@ module.exports = React.createClass({ cli.on("RoomMember.name", this.onRoomMemberName); cli.on("accountData", this.onAccountData); - // lookup for which lists a given roomId is currently in. - this.listsForRoomId = {}; - this.refreshRoomList(); // order of the sublists @@ -260,7 +257,6 @@ module.exports = React.createClass({ // ", prevMembership = " + me.events.member.getPrevContent().membership); if (me.membership == "invite") { - self.listsForRoomId[room.roomId].push("im.vector.fake.invite"); lists["im.vector.fake.invite"].push(room); } else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) { @@ -277,22 +273,18 @@ module.exports = React.createClass({ var tagName = tagNames[i]; lists[tagName] = lists[tagName] || []; lists[tagName].push(room); - self.listsForRoomId[room.roomId].push(tagName); otherTagNames[tagName] = 1; } } else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { // "Direct Message" rooms (that we're still in and that aren't otherwise tagged) - self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); lists["im.vector.fake.direct"].push(room); } else { - self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); lists["im.vector.fake.recent"].push(room); } } else if (me.membership === "leave") { - self.listsForRoomId[room.roomId].push("im.vector.fake.archived"); lists["im.vector.fake.archived"].push(room); } else { @@ -313,10 +305,8 @@ module.exports = React.createClass({ const me = room.getMember(MatrixClientPeg.get().credentials.userId); if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { - self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); lists["im.vector.fake.direct"].push(room); } else { - self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); lists["im.vector.fake.recent"].push(room); } } From 11799b4c71330f8dd0636a386e7e6c2782e20f52 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 15:27:54 +0100 Subject: [PATCH 063/416] Show "Password" instead of "New Password" when the existing password has been cached --- src/components/views/settings/ChangePassword.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index bfc9ac264e..25af0e389f 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -193,12 +193,14 @@ module.exports = React.createClass({ switch (this.state.phase) { case this.Phases.Edit: + const passwordLabel = this.state.cachedPassword ? + 'Password' : 'New Password'; return (
{ currentPassword }
- +
From 91edc064416c202de31bf18ccfcc14b499ce6ee0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 17:04:42 +0100 Subject: [PATCH 064/416] Use RVS to indicate "joining" when setting a mxid This prevents RoomView from doing any peeking whilst the join/registration is in progress, causing weirdness with TimelinePanel getPendingEventList (which throws an error if called when peeking). --- src/components/structures/RoomView.js | 14 ++++++++++---- src/stores/RoomViewStore.js | 11 ++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index e5e38a33d8..6e2a7df5ac 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -671,10 +671,6 @@ module.exports = React.createClass({ // compatability workaround, let's not bother. Rooms.setDMRoom(this.state.room.roomId, me.events.member.getSender()).done(); } - - this.setState({ - joining: false - }); } }, 500), @@ -762,12 +758,22 @@ module.exports = React.createClass({ }, }); + // Don't peek whilst registering otherwise getPendingEventList complains + // Do this by indicating our intention to join + dis.dispatch({ + action: 'will_join', + }); + const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); const close = Modal.createDialog(SetMxIdDialog, { homeserverUrl: cli.getHomeserverUrl(), onFinished: (submitted, credentials) => { if (submitted) { this.props.onRegistered(credentials); + } else { + dis.dispatch({ + action: 'cancel_join', + }); } }, onDifferentServerClicked: (ev) => { diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index fe57079859..1ceef551a8 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -58,7 +58,16 @@ class RoomViewStore extends Store { case 'view_room': this._viewRoom(payload); break; - + case 'will_join': + this._setState({ + joining: true, + }); + break; + case 'cancel_join': + this._setState({ + joining: false, + }); + break; // join_room: // - opts: options for joinRoom case 'join_room': From 0849b0e20527ae3c2abf563a0232609ae78550cc Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 17:10:49 +0100 Subject: [PATCH 065/416] Fix view_next_room, view_previous_room and view_indexed_room These must now make a dispatch to RoomViewStore instead of calling `viewRoom` directly on MatrixChat. This will call both `viewRoom` of MatrixChat _and_ the logic in RVS so there is some redundancy here. It'd be best to move as much as possible of viewRoom out to the RVS itself. But for now, this fixes a bug that occures when leaving (the viewed room would not change). --- src/components/structures/MatrixChat.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index a6f2ee820f..2e4a3b90ad 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -553,6 +553,7 @@ module.exports = React.createClass({ this.notifyNewScreen('register'); }, + // TODO: Move to RoomViewStore _viewNextRoom: function(roomIndexDelta) { const allRooms = RoomListSorter.mostRecentActivityFirst( MatrixClientPeg.get().getRooms(), @@ -566,15 +567,22 @@ module.exports = React.createClass({ } roomIndex = (roomIndex + roomIndexDelta) % allRooms.length; if (roomIndex < 0) roomIndex = allRooms.length - 1; - this._viewRoom({ room_id: allRooms[roomIndex].roomId }); + dis.dispatch({ + action: 'view_room', + room_id: allRooms[roomIndex].roomId, + }); }, + // TODO: Move to RoomViewStore _viewIndexedRoom: function(roomIndex) { const allRooms = RoomListSorter.mostRecentActivityFirst( MatrixClientPeg.get().getRooms(), ); if (allRooms[roomIndex]) { - this._viewRoom({ room_id: allRooms[roomIndex].roomId }); + dis.dispatch({ + action: 'view_room', + room_id: allRooms[roomIndex].roomId, + }); } }, From 263a51938d894896306fab155737aecc7c74b04a Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 25 May 2017 17:16:16 +0100 Subject: [PATCH 066/416] Reset store state when logging out This prevents leaking of state that we do not want to share with the next user --- src/stores/LifecycleStore.js | 5 +++++ src/stores/RoomViewStore.js | 3 +++ src/stores/SessionStore.js | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 43e2de9d52..5dfe82500a 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -61,6 +61,11 @@ class LifecycleStore extends Store { }); dis.dispatch(deferredAction); break; + case 'on_logged_out': + this._state = { + deferred_action: null, + }; + break; } } } diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 1ceef551a8..d893318af7 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -73,6 +73,9 @@ class RoomViewStore extends Store { case 'join_room': this._joinRoom(payload); break; + case 'on_logged_out': + this.reset(); + break; } } diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index 2fd35ce40a..5713e4d321 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -66,6 +66,11 @@ class SessionStore extends Store { cachedPassword: null, }); break; + case 'on_logged_out': + this._state = { + cachedPassword: null, + }; + break; } } From b5b157a0fb0bae097fc0cb62af5169b59eae31f4 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 10:34:36 +0100 Subject: [PATCH 067/416] Don't show notif nag bar if guest --- src/components/structures/LoggedInView.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 8cd2bf8a71..e7c1e00008 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -253,6 +253,7 @@ export default React.createClass({ break; } + const isGuest = this.props.matrixClient.isGuest(); var topBar; if (this.props.hasNewVersion) { topBar = ; } else if (this.state.userHasGeneratedPassword) { topBar = ; - } else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) { + } else if (!isGuest && Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) { topBar = ; } From 2dcc03960a5b9fb362e3fd6a140cc28a61c85344 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 11:46:33 +0100 Subject: [PATCH 068/416] Set the displayname to the mxid once PWLU --- src/components/structures/MatrixChat.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index a6f2ee820f..6d827b3ef0 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -805,6 +805,12 @@ module.exports = React.createClass({ dis.dispatch({action: 'view_home_page'}); } else if (this._is_registered) { this._is_registered = false; + + // Set the display name = entered username + MatrixClientPeg.get().setDisplayName( + MatrixClientPeg.get().getUserIdLocalpart() + ); + if (this.props.config.welcomeUserId) { createRoom({ dmUserId: this.props.config.welcomeUserId, From c0f43a14fd4c5b61bfe05e820a0091d4afa951d8 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 11:47:55 +0100 Subject: [PATCH 069/416] Improve comment --- src/components/structures/MatrixChat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 6d827b3ef0..3c23f649e3 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -806,7 +806,7 @@ module.exports = React.createClass({ } else if (this._is_registered) { this._is_registered = false; - // Set the display name = entered username + // Set the display name = user ID localpart MatrixClientPeg.get().setDisplayName( MatrixClientPeg.get().getUserIdLocalpart() ); From 2400efa92bc2f3622f02a0492102182ff935b613 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 11:48:38 +0100 Subject: [PATCH 070/416] Correct LifecycleStore docs --- src/stores/LifecycleStore.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 43e2de9d52..3ed2e5c045 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -20,13 +20,6 @@ import {Store} from 'flux/utils'; * A class for storing application state to do with login/registration. This is a simple * flux store that listens for actions and updates its state accordingly, informing any * listeners (views) of state changes. - * - * Usage: - * ``` - * lifecycleStore.addListener(() => { - * this.setState({ cachedPassword: lifecycleStore.getCachedPassword() }) - * }) - * ``` */ class LifecycleStore extends Store { constructor() { From ad3373789fe094d5790119aaf5002205f1d92eb1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 11:50:32 +0100 Subject: [PATCH 071/416] Warn about LifecycleStore not explicitly being used --- src/components/structures/MatrixChat.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index a6f2ee820f..ef2882fe13 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -32,6 +32,7 @@ import sdk from '../../index'; import * as Rooms from '../../Rooms'; import linkifyMatrix from "../../linkify-matrix"; import * as Lifecycle from '../../Lifecycle'; +// LifecycleStore is not used but does listen to and dispatch actions import LifecycleStore from '../../stores/LifecycleStore'; import RoomViewStore from '../../stores/RoomViewStore'; import PageTypes from '../../PageTypes'; From 28094a9a6634e5ec827bcf8481448b733bf44430 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 13:13:57 +0100 Subject: [PATCH 072/416] Show "Something went wrong!" when errcode undefined --- src/components/views/dialogs/SetMxIdDialog.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 86b5fccbc2..72599a6b21 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -108,6 +108,7 @@ export default React.createClass({ const newState = { usernameCheckSupport: err.errcode !== "M_UNRECOGNIZED", }; + console.error('Error whilst checking username availability: ', err); switch (err.errcode) { case "M_USER_IN_USE": newState.usernameError = 'Username not available'; @@ -120,8 +121,11 @@ export default React.createClass({ // fine and rely on the error appearing in registration step. newState.usernameError = ''; break; + case undefined: + newState.usernameError = 'Something went wrong!'; + break; default: - newState.usernameError = 'An error occurred' + err.message; + newState.usernameError = 'An error occurred: ' + err.message; break; } this.setState(newState); From 5e136863b0558d1edc83b1413abbef56b4a19742 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 13:18:44 +0100 Subject: [PATCH 073/416] Block user settings with view_set_mxid --- src/components/structures/MatrixChat.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index ef2882fe13..a03273c1c6 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -433,6 +433,10 @@ module.exports = React.createClass({ this._viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': + if (MatrixClientPeg.get().isGuest()) { + dis.dispatch({action: 'view_set_mxid'}); + break; + } this._setPage(PageTypes.UserSettings); this.notifyNewScreen('settings'); break; @@ -441,7 +445,6 @@ module.exports = React.createClass({ dis.dispatch({action: 'view_set_mxid'}); break; } - Modal.createDialog(TextInputDialog, { title: "Create Room", description: "Room name (optional)", From 9311b9012a4177c501a61c34a5d7d08b48f054f5 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 17:23:02 +0100 Subject: [PATCH 074/416] Use the same `.reset` as RoomViewStore --- src/stores/LifecycleStore.js | 16 ++++++++++------ src/stores/SessionStore.js | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 5dfe82500a..f7e3ff9dbb 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -16,6 +16,10 @@ limitations under the License. import dis from '../dispatcher'; import {Store} from 'flux/utils'; +const INITIAL_STATE = { + deferred_action: null, +}; + /** * A class for storing application state to do with login/registration. This is a simple * flux store that listens for actions and updates its state accordingly, informing any @@ -33,9 +37,7 @@ class LifecycleStore extends Store { super(dis); // Initialise state - this._state = { - deferred_action: null, - }; + this._state = INITIAL_STATE; } _setState(newState) { @@ -62,12 +64,14 @@ class LifecycleStore extends Store { dis.dispatch(deferredAction); break; case 'on_logged_out': - this._state = { - deferred_action: null, - }; + this.reset(); break; } } + + reset() { + this._state = Object.assign({}, INITIAL_STATE); + } } let singletonLifecycleStore = null; diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index 5713e4d321..a4b49d9cea 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -16,6 +16,10 @@ limitations under the License. import dis from '../dispatcher'; import {Store} from 'flux/utils'; +const INITIAL_STATE = { + cachedPassword: localStorage.getItem('mx_pass'), +}; + /** * A class for storing application state to do with the session. This is a simple flux * store that listens for actions and updates its state accordingly, informing any @@ -33,9 +37,7 @@ class SessionStore extends Store { super(dis); // Initialise state - this._state = { - cachedPassword: localStorage.getItem('mx_pass'), - }; + this._state = INITIAL_STATE; } _update() { @@ -67,11 +69,13 @@ class SessionStore extends Store { }); break; case 'on_logged_out': - this._state = { - cachedPassword: null, - }; + this.reset(); break; } + + reset() { + this._state = Object.assign({}, INITIAL_STATE); + } } getCachedPassword() { From ac44151e2a2d8c9b158d8b8315ddb939430291f6 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 26 May 2017 17:27:31 +0100 Subject: [PATCH 075/416] Put the reset method in the right scope... --- src/stores/SessionStore.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index a4b49d9cea..62868e4fe4 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -72,10 +72,10 @@ class SessionStore extends Store { this.reset(); break; } + } - reset() { - this._state = Object.assign({}, INITIAL_STATE); - } + reset() { + this._state = Object.assign({}, INITIAL_STATE); } getCachedPassword() { From 1efc5c2b2599120849ef7c01ebdef5d8941d4c15 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 28 May 2017 23:28:29 +0100 Subject: [PATCH 076/416] speed up SetMxIdDialog user check to 250ms as it was driving me MAD i18nize new bottomleftmenu buttons --- src/components/views/dialogs/SetMxIdDialog.js | 2 +- src/components/views/elements/CreateRoomButton.js | 3 ++- src/components/views/elements/HomeButton.js | 3 ++- src/components/views/elements/RoomDirectoryButton.js | 3 ++- src/components/views/elements/SettingsButton.js | 3 ++- src/components/views/elements/StartChatButton.js | 3 ++- src/i18n/strings/en_EN.json | 7 ++++++- 7 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 72599a6b21..d0aa067a70 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -23,7 +23,7 @@ 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; +const USERNAME_CHECK_DEBOUNCE_MS = 250; /** * Prompt the user to set a display name. diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js index 82643559b3..f98974b489 100644 --- a/src/components/views/elements/CreateRoomButton.js +++ b/src/components/views/elements/CreateRoomButton.js @@ -17,13 +17,14 @@ limitations under the License. import React from 'react'; import sdk from '../../../index'; import PropTypes from 'prop-types'; +import { _t } from '../../../languageHandler'; const CreateRoomButton = function(props) { const ActionButton = sdk.getComponent('elements.ActionButton'); return ( Date: Mon, 29 May 2017 01:32:31 +0100 Subject: [PATCH 077/416] add login link to SetMxIdDialog --- src/components/structures/MatrixChat.js | 4 ++++ src/components/structures/RoomView.js | 4 ++++ src/components/views/dialogs/SetMxIdDialog.js | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index c48a3731ae..1adb67887c 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -682,6 +682,10 @@ module.exports = React.createClass({ dis.dispatch({action: 'start_registration'}); close(); }, + onLoginClick: (ev) => { + dis.dispatch({action: 'start_login'}); + close(); + }, }).close; }, diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7ed5ea6eab..6e853c135a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -788,6 +788,10 @@ module.exports = React.createClass({ dis.dispatch({action: 'start_registration'}); close(); }, + onLoginClick: (ev) => { + dis.dispatch({action: 'start_login'}); + close(); + }, }).close; return; } diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index d0aa067a70..19924093de 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -36,6 +36,8 @@ export default React.createClass({ onFinished: React.PropTypes.func.isRequired, // Called when the user requests to register with a different homeserver onDifferentServerClicked: React.PropTypes.func.isRequired, + // Called if the user wants to switch to login instead + onLoginClick: React.PropTypes.func.isRequired, }, getInitialState: function() { @@ -245,6 +247,9 @@ export default React.createClass({ different server .

+

+ If you already have a Matrix account you can log in instead. +

{ auth } { authErrorIndicator }
From 4afba2f7964ffcceafce93d1aaf35ab8796a5476 Mon Sep 17 00:00:00 2001 From: "saul.kredi@krutt.org" Date: Mon, 29 May 2017 18:44:39 +0300 Subject: [PATCH 078/416] Add support for RTL languages --- src/HtmlUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 4acb314c2f..8af1894c79 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -360,7 +360,7 @@ export function bodyToHtml(content, highlights, opts) { 'mx_EventTile_bigEmoji': emojiBody, 'markdown-body': isHtml, }); - return ; + return ; } export function emojifyText(text) { From ad1b14967bfb06614c0b04e7a3dfe1483380225b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 30 May 2017 03:59:06 +0100 Subject: [PATCH 079/416] hide rightpanel on welcome page --- src/components/structures/LoggedInView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index f630503500..a297952a2c 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -244,7 +244,6 @@ export default React.createClass({ teamToken={this.props.teamToken} homePageUrl={this.props.config.welcomePageUrl} />; - if (!this.props.collapse_rhs) right_panel = break; case PageTypes.UserView: From 952651c6858b40f425c5efad86fcdb7558ab70d0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 30 May 2017 13:02:35 +0100 Subject: [PATCH 080/416] Allow pressing Enter to submit setMxId --- src/components/views/dialogs/SetMxIdDialog.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 19924093de..61acbf2192 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -91,6 +91,12 @@ export default React.createClass({ }); }, + onKeyUp: function(ev) { + if (ev.keyCode === 13) { + this.onSubmit(); + } + }, + onSubmit: function(ev) { this.setState({ doingUIAuth: true, @@ -233,7 +239,10 @@ export default React.createClass({
{ usernameBusyIndicator } From 47bf5401fa06cc074bb6818b7b5c98c11c8de54a Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 30 May 2017 13:14:14 +0100 Subject: [PATCH 081/416] Use KeyCode.ENTER instead of 13 --- src/components/views/dialogs/SetMxIdDialog.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 61acbf2192..efca192ec1 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -20,6 +20,7 @@ import React from 'react'; import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; import classnames from 'classnames'; +import KeyCode from '../../../KeyCode'; // The amount of time to wait for further changes to the input username before // sending a request to the server @@ -92,7 +93,7 @@ export default React.createClass({ }, onKeyUp: function(ev) { - if (ev.keyCode === 13) { + if (ev.keyCode === KeyCode.ENTER) { this.onSubmit(); } }, From 2baef643e3ee1a2a9e8a21f1778ab7aa8d237d93 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 30 May 2017 14:27:02 +0100 Subject: [PATCH 082/416] Add /start to show the setMxId above HomePage --- src/components/structures/MatrixChat.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1adb67887c..e5fab4dde7 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -685,7 +685,7 @@ module.exports = React.createClass({ onLoginClick: (ev) => { dis.dispatch({action: 'start_login'}); close(); - }, + }, }).close; }, @@ -991,6 +991,11 @@ module.exports = React.createClass({ dis.dispatch({ action: 'view_home_page', }); + } else if (screen == 'start') { + this.showScreen('home'); + dis.dispatch({ + action: 'view_set_mxid', + }); } else if (screen == 'directory') { dis.dispatch({ action: 'view_room_directory', From 6f8d5b1db3dc088767640e6996fa29aac6cc4864 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 30 May 2017 16:10:19 +0100 Subject: [PATCH 083/416] Remove spurious reference to `otherTagNames` --- src/components/views/rooms/RoomList.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index d62ce4d460..0bca41e9e4 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -274,7 +274,6 @@ module.exports = React.createClass({ var tagName = tagNames[i]; lists[tagName] = lists[tagName] || []; lists[tagName].push(room); - otherTagNames[tagName] = 1; } } else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { From 40154df9304877daccf6da1b46cd2e6db09d1d6a Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 30 May 2017 16:14:27 +0100 Subject: [PATCH 084/416] Show People/Rooms emptySubListTip even when total rooms !== 0 --- src/components/views/rooms/RoomList.js | 35 +++++++++++++------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 0bca41e9e4..d672dea504 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -510,24 +510,23 @@ module.exports = React.createClass({ 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 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 +
; } if (this.state.totalRoomCount === 0) { From c15568e003a9a32c37bf6600b796698cea66cd41 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 30 May 2017 21:05:26 +0100 Subject: [PATCH 085/416] remove spurious string --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 15879f80ee..11727223e3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -583,7 +583,6 @@ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", "Set a display name:": "Set a display name:", - "Set a Display Name": "Set a Display Name", "Upload an avatar:": "Upload an avatar:", "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", "Missing password.": "Missing password.", From 12f923bc81c8ae95d790b65b665bc232f35661b2 Mon Sep 17 00:00:00 2001 From: daniel tygel Date: Tue, 30 May 2017 18:56:52 -0300 Subject: [PATCH 086/416] add new string to translate --- src/components/views/rooms/TopUnreadMessagesBar.js | 2 +- src/i18n/strings/en_EN.json | 1 + src/i18n/strings/pt_BR.json | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js index 6cb6475a8f..45aba66633 100644 --- a/src/components/views/rooms/TopUnreadMessagesBar.js +++ b/src/components/views/rooms/TopUnreadMessagesBar.js @@ -37,7 +37,7 @@ module.exports = React.createClass({ { - Jump to first unread message. + { _t("Jump to first unread message.") }
Date: Tue, 30 May 2017 19:01:23 -0300 Subject: [PATCH 087/416] add string to translate --- src/components/views/rooms/EventTile.js | 2 +- src/i18n/strings/en_EN.json | 1 + src/i18n/strings/pt_BR.json | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 6db42a4b9d..d9a6925793 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -481,7 +481,7 @@ module.exports = WithMatrixClient(React.createClass({ } const editButton = ( - + ); let e2e; // cosmetic padlocks: diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 73774c52c6..ce2a630bc0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -677,6 +677,7 @@ "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar", "Analytics": "Analytics", "Opt out of analytics": "Opt out of analytics", + "Options": "Options", "Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.", "Please select the destination room for this message": "Please select the destination room for this message", "Passphrases must match": "Passphrases must match", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 49ef774dac..9a79219a48 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -729,6 +729,7 @@ "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s apagou o nome da sala.", "Analytics": "Análise", "Opt out of analytics": "Sair da ferramenta de análise", + "Options": "Opções", "Riot collects anonymous analytics to allow us to improve the application.": "Riot coleta informações anônimas de uso para nos permitir melhorar o sistema.", "Passphrases must match": "As senhas têm que ser iguais", "Passphrase must not be empty": "A senha não pode estar vazia", From 0c918f242fe4a08dcabdd81290c6f536f7b00769 Mon Sep 17 00:00:00 2001 From: Amandine Date: Tue, 30 May 2017 23:47:42 +0000 Subject: [PATCH 088/416] Translated using Weblate (French) Currently translated at 97.7% (751 of 768 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 3454148f07..b8c844bd27 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -678,7 +678,7 @@ "Please select the destination room for this message": "Merci de sélectionner un salon de destination pour ce message", "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s a supprimé le nom du salon.", "Analytics": "Outils d'analyse", - "Opt out of analytics": "Refus de participation", + "Opt out of analytics": "Refuser de participer", "Riot collects anonymous analytics to allow us to improve the application.": "Riot recueille des données anonymes qui nous permettent d’analyser et améliorer l’application.", "Passphrases must match": "Les phrases secrètes doivent être identiques", "Passphrase must not be empty": "La phrase secrète ne doit pas être vide", From a72fe7b2f59e730927109dbfd177a4bde3538be3 Mon Sep 17 00:00:00 2001 From: Krombel Date: Tue, 30 May 2017 23:53:12 +0000 Subject: [PATCH 089/416] Translated using Weblate (German) Currently translated at 100.0% (768 of 768 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 63222c53b6..b0ae13a5f8 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -715,7 +715,7 @@ "olm version:": "Version von olm:", "Passwords can't be empty": "Passwörter dürfen nicht leer sein", "Registration required": "Registrierung benötigt", - "Report it": "Melde es", + "Report it": "Melde ihn", "riot-web version:": "Version von riot-web:", "Scroll to bottom of page": "Zum Ende der Seite springen", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Zeige Zeitstempel im 12-Stunden-Format (z. B. 2:30pm)", From d83f18ab4669ae2d9b80ef06c3b16c698e5df4b0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 31 May 2017 10:03:16 +0100 Subject: [PATCH 090/416] Remove cachedPassword from localStorage on_logged_out Fixes https://github.com/vector-im/riot-web/issues/4101 --- src/stores/SessionStore.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index 62868e4fe4..6ec8ae7697 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -69,7 +69,9 @@ class SessionStore extends Store { }); break; case 'on_logged_out': - this.reset(); + this._setState({ + cachedPassword: null, + }); break; } } From 117e6b8fc3b16244c4e9fee8e9cb94a4d5492d44 Mon Sep 17 00:00:00 2001 From: Jean GB Date: Wed, 31 May 2017 09:46:43 +0000 Subject: [PATCH 091/416] Translated using Weblate (French) Currently translated at 99.2% (762 of 768 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 | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index b8c844bd27..3d85aefbc7 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -753,5 +753,16 @@ "Image '%(Body)s' cannot be displayed.": "L'image '%(Body)s' ne peut être affichée.", "This image cannot be displayed.": "Cette image ne peut être affichée.", "Error decrypting video": "Erreur lors de la décryption de la vidéo", - "Add an Integration": "Ajouter une intégration" + "Add an Integration": "Ajouter une intégration", + "URL Previews": "Aperçus d'URL", + "Disable URL previews by default for participants in this room": "Désactiver les aperçus d'URL par défaut pour les participants de ce salon", + "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "Les aperçus d'URL sont %(globalDisableUrlPreview)s par défaut pour les participants de ce salon.", + "Enable URL previews for this room (affects only you)": "Activer les aperçus d'URL pour ce salon (n'affecte que vous)", + "Drop file here to upload": "Déposer le fichier ici pour téléchargement", + " (unsupported)": " (non supporté)", + "Ongoing conference call%(supportedText)s. %(joinText)s": "Appel conférence en cours%(supportedText)s. %(joinText)s", + "Online": "En ligne", + "Offline": "Hors ligne", + "Disable URL previews for this room (affects only you)": "Désactiver les aperçus d'URL pour ce salon (n'affecte que vous)", + "Desktop specific": "Spécifique à la version bureau" } From dbf5ec632e7800878506073ec9294792dab3c28a Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 31 May 2017 08:50:24 +0000 Subject: [PATCH 092/416] Translated using Weblate (German) Currently translated at 100.0% (768 of 768 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 | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index b0ae13a5f8..89e7f97515 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -16,7 +16,7 @@ "Blacklisted": "Blockiert", "verified": "verifiziert", "Name": "Name", - "Device ID": "Geräte ID", + "Device ID": "Geräte-ID", "Verification": "Verifizierung", "Ed25519 fingerprint": "Ed25519 Fingerprint", "User ID": "Benutzer-ID", @@ -38,7 +38,7 @@ "Kicks user with given id": "Kickt Benutzer mit angegebener ID", "Changes your display nickname": "Ändert deinen angezeigten Nicknamen", "Change Password": "Passwort ändern", - "Searches DuckDuckGo for results": "Suche in DuckDuckGo nach Ergebnissen", + "Searches DuckDuckGo for results": "Verwendet DuckDuckGo für Suchergebnisse", "Commands": "Kommandos", "Emoji": "Smileys", "Sorry, this homeserver is using a login which is not recognised ": "Entschuldigung, dieser Homeserver nutzt eine Anmeldetechnik, die nicht bekannt ist ", @@ -79,9 +79,9 @@ "Confirm your new password": "Bestätige dein neues Passwort", "Continue": "Fortfahren", "Create an account": "Erstelle einen Account", - "Create Room": "Erstelle Raum", + "Create Room": "Raum erstellen", "Cryptography": "Kryptografie", - "Deactivate Account": "Deaktiviere Account", + "Deactivate Account": "Account deaktivieren", "Deactivate my account": "Deaktiviere meinen Account", "decline": "Ablehnen", "Devices will not yet be able to decrypt history from before they joined the room": "Geräte werden nicht in der Lage sein, die Historie vor dem Beitritt in den Raum zu entschlüsseln", @@ -102,7 +102,7 @@ "all room members": "Alle Raum-Mitglieder", "all room members, from the point they are invited": "Alle Raum-Mitglieder, ab dem Zeitpunkt, an dem sie eingeladen werden", "answered the call.": "beantwortete den Anruf.", - "Can't load user settings": "Kann Nutzereinstellungen nicht laden", + "Can't load user settings": "Benutzereinstellungen können nicht geladen werden", "changed name": "änderte Namen", "changed the power level of": "änderte Berechtigungslevel von", "Clear Cache": "Leere Cache", @@ -123,7 +123,7 @@ "Forget room": "Raum vergessen", "Forgot your password?": "Passwort vergessen?", "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Aus Sicherheitsgründen werden beim Ausloggen alle Ende-zu-Ende-Verschlüsselungs-Schlüssel in diesem Browser gelöscht. Wenn du in späteren Riot-Sitzungen die Konversationshistorie entschlüsseln möchtest, exportiere bitte deine Schlüssel zur sicheren Verwahrung.", - "For security, this session has been signed out. Please sign in again.": "Zur Sicherheit wurde diese Sitzung abgemeldet. Bitte melde dich erneut an.", + "For security, this session has been signed out. Please sign in again.": "Aus Sicherheitsgründen wurde diese Sitzung beendet. Bitte melde dich erneut an.", "Found a bug?": "Fehler gefunden?", "Guests cannot join this room even if explicitly invited.": "Gäste können diesem Raum nicht beitreten auch wenn sie explizit eingeladen werden.", "Guests can't set avatars. Please register.": "Gäste können keine Avatare setzen. Bitte registriere dich.", @@ -179,7 +179,7 @@ "Profile": "Profil", "Refer a friend to Riot:": "Freunde zu Riot einladen:", "rejected": "abgelehnt", - "Once you've followed the link it contains, click below": "Nachdem du dem Link gefolgt bist, klicke unten", + "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?": "Lösche Kontakt-Informationen?", @@ -188,7 +188,7 @@ "requested a VoIP conference": "hat eine VoIP-Konferenz angefordert", "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Eine Passwortänderung sorgt aktuell dafür, dass alle Ende-zu-Ende-Schlüssel von allen Geräten zurückgesetzt werden. Dadurch wird die verschlüsselte Chat-Historie unlesbar, es sei denn Sie exportieren vorher Ihre Raum-Schlüssel und importieren sie nachher wieder. In Zukunft wird dies verbessert.", "restore": "Zum zurücksetzen", - "Return to login screen": "Zur Anmeldung zurückkehren", + "Return to login screen": "Zur Anmeldemaske zurückkehren", "Room Colour": "Raumfarbe", "Room name (optional)": "Raumname (optional)", "Scroll to unread messages": "Scrolle zu ungelesenen Nachrichten", @@ -208,7 +208,7 @@ "since they were invited": "seitdem sie eingeladen wurden", "Someone": "Jemand", "Start a chat": "Starte einen Chat", - "Start Chat": "Starte Chat", + "Start Chat": "Chat beginnen", "Success": "Erfolg", "tag direct chat": "Zum kennzeichnen als direkten Chat", "The default role for new room members is": "Die Standard-Rolle für neue Raum-Mitglieder ist", @@ -224,13 +224,13 @@ "To ban users": "Zum Nutzer bannen", "To configure the room": "Zum Raum konfigurieren", "To invite users into the room": "Zum Nutzer in den Raum einladen", - "to join the discussion": "Zum mitdiskutieren", - "To kick users": "Zum Nutzer kicken", + "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", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar": "Verbindung zum Homeserver ist über HTTP nicht möglich, wenn HTTPS in deiner Browserleiste steht", "Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen", - "Disable inline URL previews by default": "Deaktiviere URL-Vorschau im Chat standardmäßig", + "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.", "Labs": "Labor", "Show panel": "Zeige Panel", @@ -261,7 +261,7 @@ "VoIP conference finished.": "VoIP-Konferenz beendet.", "VoIP conference started.": "VoIP-Konferenz gestartet.", "(warning: cannot be disabled again!)": "(Warnung: Kann nicht wieder deaktiviert werden!)", - "was banned": "wurde gebannt", + "was banned": "wurde aus dem Raum verbannt", "was invited": "wurde eingeladen", "was kicked": "wurde gekickt", "was unbanned": "wurde entbannt", @@ -353,7 +353,7 @@ "%(names)s and one other are typing": "%(names)s und eine weitere Person tippen", "%(names)s and %(count)s others are typing": "%(names)s und %(count)s weitere Personen tippen", "%(senderName)s answered the call.": "%(senderName)s beantwortete den Anruf.", - "%(senderName)s banned %(targetName)s.": "%(senderName)s bannte %(targetName)s.", + "%(senderName)s 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 änderte den Anzeigenamen von %(oldDisplayName)s zu %(displayName)s.", "%(senderName)s changed their profile picture.": "%(senderName)s änderte das Profilbild.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s.", @@ -474,7 +474,7 @@ "es-ec": "Spanisch (Ecuador)", "es-gt": "Spanisch (Guatemala)", "es-hn": "Spanisch (Honduras)", - "es-mx": "Spanisch (Mexico)", + "es-mx": "Spanisch (Mexiko)", "es-ni": "Spanisch (Nicaragua)", "es-pa": "Spanisch (Panama)", "es-pe": "Spanisch (Peru)", @@ -514,7 +514,7 @@ "pl": "Polnisch", "pt": "Portugiesisch", "rm": "Rätoromanisch", - "ro-mo": "Rumänisch (Republik von Moldavien)", + "ro-mo": "Rumänisch (Republik Moldau/Moldawien)", "ro": "Romanian", "ru-mo": "Russisch", "sb": "Sorbisch", @@ -576,7 +576,7 @@ "Failed to ban user": "Bannen des Nutzers fehlgeschlagen", "Failed to change power level": "Ändern des Berechtigungslevels fehlgeschlagen", "Failed to delete device": "Löschen des Geräts fehlgeschlagen", - "Failed to join room": "Raumbeitritt fehlgeschlagen", + "Failed to join room": "Betreten des Raumes ist fehlgeschlagen", "Failed to kick": "Kicken fehlgeschlagen", "Failed to mute user": "Nutzer lautlos zu stellen fehlgeschlagen", "Failed to reject invite": "Einladung abzulehnen fehlgeschlagen", @@ -585,7 +585,7 @@ "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": "Verifikationscode inkorrekt", + "Incorrect verification code": "Falscher Verifizierungsscode", "Invalid alias format": "Ungültiges Alias-Format", "Invalid address format": "Ungültiges Adressformat", "'%(alias)s' is not a valid format for an address": "'%(alias)s' ist kein gültiges Adressformat", @@ -671,7 +671,7 @@ "%(severalUsers)sleft and rejoined": "%(severalUsers)s gingen und traten erneut bei", "%(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", - "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)s lehnte seine/ihre Einladung %(repeats)s mal ab", + "%(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)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)szogen ihre Einladungen %(repeats)s mal zurück", @@ -713,7 +713,7 @@ "matrix-react-sdk version:": "Version von matrix-react-sdk:", "New passwords don't match": "Neue Passwörter nicht gleich", "olm version:": "Version von olm:", - "Passwords can't be empty": "Passwörter dürfen nicht leer sein", + "Passwords can't be empty": "Passwortfelder dürfen nicht leer sein", "Registration required": "Registrierung benötigt", "Report it": "Melde ihn", "riot-web version:": "Version von riot-web:", @@ -756,7 +756,7 @@ "I verify that the keys match": "Ich bestätige, dass die Schlüssel passen", "Unable to restore session": "Sitzungswiederherstellung fehlgeschlagen", "Continue anyway": "Fahre trotzdem fort", - "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Dein Anzeigename ist dein Name der anderen gezeigt wird, wenn du in Räumen sprichst. Wie möchtest du ihn haben?", + "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Dein Anzeigename ist der Name, der anderen Nutzern angezeigt wird, wenn du in Räumen sprichst. Welchen Anzeigenamen möchtest du wählen?", "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "Do blockst aktuell unverifizierte Geräte. Um Nachrichten an diese Geräte zu senden musst du sie verifizieren.", "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" enthält Geräte die du noch nicht gesehen hast.", "Unknown devices": "Unbekannte Geräte", @@ -795,7 +795,7 @@ "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.": "Die exportierte Datei erlaubt jedem, der diese lesen kann, jede verschlüsselte Nachricht zu entschlüsseln die du sehen kannst. Du solltest sie also sicher verwahren. Um dabei zu helfen, solltest du unten eine Passphrase eingeben, die dazu verwendet wird, die exportierten Daten zu verschlüsseln. Anschließend ist es nur möglich die Daten zu lesen, wenn dieselbe Passphrase verwendet wird.", "Analytics": "Analyse", "Opt out of analytics": "Zustimmung zur Analyse verweigern", - "Riot collects anonymous analytics to allow us to improve the application.": "Riot sammeln anonyme Daten damit wir die Anwendung verbessern können.", + "Riot collects anonymous analytics to allow us to improve the application.": "Riot sammelt anonymisierte Analysedaten, um die Anwendung kontinuierlich verbessern zu können.", "Add an Integration": "Eine Integration hinzufügen", "Removed or unknown message type": "Gelöschte oder unbekannter Nachrichten-Typ", "Disable URL previews by default for participants in this room": "Deaktiviere standardmäßig die URL-Vorschau für Teilnehmer dieses Raumes", @@ -809,7 +809,7 @@ "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "Dies wird dein Konto permanent unbenutzbar machen. Du wirst dich nicht mit derselben Nutzer-ID erneut registrieren können.", "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:": "Um zu bestätigen, dass diesem Gerät vertraut werden kann, kontaktiere bitte den Eigentümer über einen anderen Weg (z.B. Telefon-Anruf) und frage, ob der Schlüssel, den sie in den Nutzer-Einstellungen für dieses Gerät sehen dem folgenden gleicht:", "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.": "Wenn er passt, betätige den Bestätigen-Button unten. Wenn nicht, fängt jemand anderes dieses Gerät ab und du möchtest wahrscheinlich lieber den Blacklist-Button betätigen.", - "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.": "Wir sind auf einen Fehler gestoßen während wir deine vorherige Sitzung wiederherstellen wollten. Wenn du fortfährst, wirst du dich erneut anmelden müssen und die verschlüsselte Chat-Historie wir unlesbar sein.", + "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.": "Bei der Wiederherstellung deiner vorherigen Sitzung ist ein Fehler aufgetreten. Um fortzufahren, musst du dich erneut anmelden. Eine zuvor verschlüsselte Chat-Historie wird in der Folge nicht mehr lesbar sein.", "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.": "Wenn du vorher eine aktuellere Version von Riot verwendet hast, ist deine Sitzung wohlmöglich inkompatibel mit dieser Version. Schließe dieses Fenster und kehre zur aktuelleren Version zurück.", "Blacklist": "Blockieren", "Unblacklist": "Entblockieren", From 3b7b1d620653cee740e4f95148a8f8596dfbfdb9 Mon Sep 17 00:00:00 2001 From: dtygel Date: Wed, 31 May 2017 12:02:08 +0000 Subject: [PATCH 093/416] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (768 of 768 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 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index ac0c7089ae..cbd44fffd8 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -812,7 +812,11 @@ "Online": "Online", "Idle": "Ocioso", "Offline": "Offline", - "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.": "", + "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.": "O arquivo exportado irá permitir a qualquer pessoa que o acesse a descriptografar qualquer uma das mensagens criptografadas que você veja, portanto seja bastante cuidadosa(o) em manter este arquivo seguro. Para deixar este arquivo mais protegido, recomendamos que você insira uma senha abaixo, que será usada para criptografar o arquivo. Só será possível importar os dados usando exatamente a mesma senha.", "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.": "Este processo faz com que você possa importar as chaves de criptografia que tinha previamente exportado de outro cliente Matrix. Você poderá então descriptografar todas as mensagens que o outro cliente pôde criptografar.", - "You are about to be taken to a third-party site so you can authenticate your account for use with {integrationsUrl}. Do you wish to continue?": "" + "You are about to be taken to a third-party site so you can authenticate your account for use with {integrationsUrl}. Do you wish to continue?": "", + "Start automatically after system login": "Iniciar automaticamente ao iniciar o sistema", + "Desktop specific": "Específico para o app de computadores desktop", + "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?": "Você será levado agora a um site de terceiros para poder autenticar a sua conta para uso com o serviço %(integrationsUrl)s. Você quer continuar?", + "Disable URL previews for this room (affects only you)": "Desabilitar as pré-visualizações de sites para esta sala (afeta apenas a você)" } From 75d2b2319d387eb348563a3e42752c93f395ccd0 Mon Sep 17 00:00:00 2001 From: daniel tygel Date: Wed, 31 May 2017 09:10:19 -0300 Subject: [PATCH 094/416] translate directly in pt_BR.json --- 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 220bd7ade5..331fda1947 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -605,7 +605,7 @@ "'%(alias)s' is not a valid format for an address": "'%(alias)s' não é um formato válido para um endereço", "'%(alias)s' is not a valid format for an alias": "'%(alias)s' não é um formato válido para um alias", "Join Room": "Ingressar na sala", - "Jump to first unread message.": "Jump to first unread message.", + "Jump to first unread message.": "Ir diretamente para a primeira das mensagens não lidas.", "Kick": "Remover", "Level": "Nível", "Local addresses for this room:": "Endereço local desta sala:", From 2d73f094ff71413698771e9eb0707083f258801f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 31 May 2017 15:09:09 +0100 Subject: [PATCH 095/416] Delint MatrixChat, again (#979) grrr. --- src/components/structures/MatrixChat.js | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1da4b9d988..64b5354eda 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -396,7 +396,7 @@ module.exports = React.createClass({ }, (err) => { modal.close(); Modal.createDialog(ErrorDialog, { - title: _t('Failed to reject invitation'), + title: _t('Failed to reject invitation'), description: err.toString(), }); }); @@ -445,8 +445,8 @@ module.exports = React.createClass({ title: _t('Create Room'), description: _t('Room name (optional)'), button: _t('Create Room'), - onFinished: (should_create, name) => { - if (should_create) { + onFinished: (shouldCreate, name) => { + if (shouldCreate) { const createOpts = {}; if (name) createOpts.name = name; createRoom({createOpts}).done(); @@ -579,44 +579,44 @@ module.exports = React.createClass({ // switch view to the given room // - // @param {Object} room_info Object containing data about the room to be joined - // @param {string=} room_info.room_id ID of the room to join. One of room_id or room_alias must be given. - // @param {string=} room_info.room_alias Alias of the room to join. One of room_id or room_alias must be given. - // @param {boolean=} room_info.auto_join If true, automatically attempt to join the room if not already a member. - // @param {boolean=} room_info.show_settings Makes RoomView show the room settings dialog. - // @param {string=} room_info.event_id ID of the event in this room to show: this will cause a switch to the + // @param {Object} roomInfo Object containing data about the room to be joined + // @param {string=} roomInfo.room_id ID of the room to join. One of room_id or room_alias must be given. + // @param {string=} roomInfo.room_alias Alias of the room to join. One of room_id or room_alias must be given. + // @param {boolean=} roomInfo.auto_join If true, automatically attempt to join the room if not already a member. + // @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 {Object=} room_info.third_party_invite Object containing data about the third party + // @param {Object=} roomInfo.third_party_invite Object containing data about the third party // we received to join the room, if any. - // @param {string=} room_info.third_party_invite.inviteSignUrl 3pid invite sign URL - // @param {string=} room_info.third_party_invite.invitedEmail The email address the invite was sent to - // @param {Object=} room_info.oob_data Object of additional data about the room + // @param {string=} roomInfo.third_party_invite.inviteSignUrl 3pid invite sign URL + // @param {string=} roomInfo.third_party_invite.invitedEmail The email address the invite was sent to + // @param {Object=} roomInfo.oob_data Object of additional data about the room // that has been passed out-of-band (eg. // room name and avatar from an invite email) - _viewRoom: function(room_info) { + _viewRoom: function(roomInfo) { this.focusComposer = true; const newState = { - initialEventId: room_info.event_id, - highlightedEventId: room_info.event_id, + initialEventId: roomInfo.event_id, + highlightedEventId: roomInfo.event_id, initialEventPixelOffset: undefined, page_type: PageTypes.RoomView, - thirdPartyInvite: room_info.third_party_invite, - roomOobData: room_info.oob_data, - currentRoomAlias: room_info.room_alias, - autoJoin: room_info.auto_join, + thirdPartyInvite: roomInfo.third_party_invite, + roomOobData: roomInfo.oob_data, + currentRoomAlias: roomInfo.room_alias, + autoJoin: roomInfo.auto_join, }; - if (!room_info.room_alias) { - newState.currentRoomId = room_info.room_id; + if (!roomInfo.room_alias) { + 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 (!room_info.event_id && this.refs.loggedInView) { - const scrollState = this.refs.loggedInView.getScrollStateForRoom(room_info.room_id); + 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; @@ -628,15 +628,15 @@ module.exports = React.createClass({ let waitFor = q(null); if (!this.firstSyncComplete) { if (!this.firstSyncPromise) { - console.warn('Cannot view a room before first sync. room_id:', room_info.room_id); + console.warn('Cannot view a room before first sync. room_id:', roomInfo.room_id); return; } waitFor = this.firstSyncPromise.promise; } waitFor.done(() => { - let presentedId = room_info.room_alias || room_info.room_id; - const room = MatrixClientPeg.get().getRoom(room_info.room_id); + let presentedId = roomInfo.room_alias || roomInfo.room_id; + const room = MatrixClientPeg.get().getRoom(roomInfo.room_id); if (room) { const theAlias = Rooms.getDisplayAliasForRoom(room); if (theAlias) presentedId = theAlias; @@ -648,8 +648,8 @@ module.exports = React.createClass({ } } - if (room_info.event_id) { - presentedId += "/" + room_info.event_id; + if (roomInfo.event_id) { + presentedId += "/" + roomInfo.event_id; } this.notifyNewScreen('room/' + presentedId); newState.ready = true; @@ -663,7 +663,7 @@ module.exports = React.createClass({ title: _t('Start a chat'), description: _t("Who would you like to communicate with?"), placeholder: _t("Email, name or matrix ID"), - button: _t("Start Chat") + button: _t("Start Chat"), }); }, From d0e270bd1c24244a5dc7cd09801dac60fd792432 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 31 May 2017 15:13:27 +0100 Subject: [PATCH 096/416] Only re-render LoggedInView if MatrixClientPeg.get() is truthy --- src/components/structures/LoggedInView.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index a297952a2c..67e2fe8856 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -24,6 +24,7 @@ import PageTypes from '../../PageTypes'; import sdk from '../../index'; import dis from '../../dispatcher'; import sessionStore from '../../stores/SessionStore'; +import MatrixClientPeg from '../../MatrixClientPeg'; /** * This is what our MatrixChat shows when we are logged in. The precise view is @@ -91,6 +92,16 @@ export default React.createClass({ } }, + // Child components assume that the client peg will not be null, so give them some + // sort of assurance here by only allowing a re-render if the client is truthy. + // + // This is required because `LoggedInView` maintains its own state and if this state + // updates after the client peg has been made null (during logout), then it will + // attempt to re-render and the children will throw errors. + shouldComponentUpdate: function() { + return Boolean(MatrixClientPeg.get()); + }, + getScrollStateForRoom: function(roomId) { return this._scrollStateMap[roomId]; }, From b3a862c2c2f0fe2c0381ea12936775d46c86d894 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 31 May 2017 15:32:55 +0100 Subject: [PATCH 097/416] Remove redundant `reset` --- src/stores/SessionStore.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index 6ec8ae7697..c4bd39b72c 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -76,10 +76,6 @@ class SessionStore extends Store { } } - reset() { - this._state = Object.assign({}, INITIAL_STATE); - } - getCachedPassword() { return this._state.cachedPassword; } From 8192374481935d79a27e61a967a965f2918e4f59 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 31 May 2017 16:02:42 +0100 Subject: [PATCH 098/416] Add missing _t import --- src/components/views/login/LoginFooter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/login/LoginFooter.js b/src/components/views/login/LoginFooter.js index a5183ffff3..8bdec71685 100644 --- a/src/components/views/login/LoginFooter.js +++ b/src/components/views/login/LoginFooter.js @@ -16,6 +16,7 @@ limitations under the License. 'use strict'; +import { _t } from '../../../languageHandler'; import React from 'react'; module.exports = React.createClass({ @@ -27,5 +28,5 @@ module.exports = React.createClass({ {_t("powered by Matrix")}
); - } + }, }); From 396545b783ecd92496b9bb0a56d0855b484c90c5 Mon Sep 17 00:00:00 2001 From: Jean GB Date: Wed, 31 May 2017 12:14:30 +0000 Subject: [PATCH 099/416] Translated using Weblate (French) Currently translated at 99.3% (763 of 768 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, 3 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 3d85aefbc7..9462a9c557 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -119,7 +119,7 @@ "zh-sg": "Chinese (Singapore)", "zh-tw": "Chinese (Taiwan)", "zu": "Zulu", - "anyone": "anyone", + "anyone": "n'importe qui", "Direct Chat": "Conversation Directe", "Direct chats": "Conversations directes", "Disable inline URL previews by default": "Désactiver l’aperçu des URLs", @@ -764,5 +764,6 @@ "Online": "En ligne", "Offline": "Hors ligne", "Disable URL previews for this room (affects only you)": "Désactiver les aperçus d'URL pour ce salon (n'affecte que vous)", - "Desktop specific": "Spécifique à la version bureau" + "Desktop specific": "Spécifique à la version bureau", + "Start automatically after system login": "Démarrer automatiquement après la phase d'authentification du système." } From 53502c9640a5678adeb1f7f344105459e06a7328 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 31 May 2017 10:48:57 +0000 Subject: [PATCH 100/416] Translated using Weblate (German) Currently translated at 100.0% (768 of 768 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 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 89e7f97515..96dd38c78f 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -40,7 +40,7 @@ "Change Password": "Passwort ändern", "Searches DuckDuckGo for results": "Verwendet DuckDuckGo für Suchergebnisse", "Commands": "Kommandos", - "Emoji": "Smileys", + "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", "Return to app": "Zurück zur Anwendung", @@ -93,7 +93,7 @@ "Encryption is enabled in this room": "Verschlüsselung ist in diesem Raum aktiviert", "Encryption is not enabled in this room": "Verschlüsselung ist in diesem Raum nicht aktiviert", "ended the call.": "beendete den Anruf.", - "End-to-end encryption is in beta and may not be reliable": "Ende-zu-Ende-Verschlüsselung ist im Beta-Status und ist evtl. nicht zuverlässig", + "End-to-end encryption is in beta and may not be reliable": "Die Ende-zu-Ende-Verschlüsselung befindet sich im Beta-Stadium und ist eventuell nicht hundertprozentig zuverlässig", "Failed to send email": "Fehler beim Senden der E-Mail", "Account": "Konto", "Add phone number": "Füge Telefonnummer hinzu", @@ -360,7 +360,7 @@ "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s.", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s änderte das Thema zu \"%(topic)s\".", "/ddg is not a command": "/ddg ist kein Kommando", - "%(senderName)s ended the call.": "%(senderName)s beendete den Anruf.", + "%(senderName)s ended the call.": "%(senderName)s hat den Anruf beendet.", "Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl", "Failed to send request.": "Anfrage zu senden schlug fehl.", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s", @@ -401,9 +401,9 @@ "Error changing language": "Fehler beim Ändern der Sprache", "Riot was unable to find the correct Data for the selected Language.": "Riot war nicht in der Lage die korrekten Daten für die ausgewählte Sprache zu finden.", "Connectivity to the server has been lost.": "Verbindung zum Server untergebrochen.", - "Sent messages will be stored until your connection has returned.": "Gesendete Nachrichten werden gespeichert bis die Verbindung wiederhergestellt wurde.", + "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": "Alles erneut senden", + "Resend all": "Alle erneut senden", "cancel all": "alles abbrechen", "now. You can also select individual messages to resend or cancel.": "jetzt. Du kannst auch einzelne Nachrichten zum erneuten Senden oder Abbrechen auswählen.", "Active call": "Aktiver Anruf", @@ -515,7 +515,7 @@ "pt": "Portugiesisch", "rm": "Rätoromanisch", "ro-mo": "Rumänisch (Republik Moldau/Moldawien)", - "ro": "Romanian", + "ro": "Rumänisch", "ru-mo": "Russisch", "sb": "Sorbisch", "sk": "Slowakisch", @@ -559,7 +559,7 @@ "and one other...": "und ein(e) weitere(r)...", "Are you sure?": "Bist du sicher?", "Attachment": "Anhang", - "Ban": "Banne", + "Ban": "Verbannen", "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Kann nicht zum Heimserver verbinden - bitte checke eine Verbindung und stelle sicher, dass dem %(urlStart)s SSL-Zertifikat deines Heimservers %(urlEnd)s vertraut wird", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Kann nicht zum Heimserver via HTTP verbinden, wenn eine HTTPS-Url in deiner Adresszeile steht. Nutzer HTTPS oder %(urlStart)s aktiviere unsichere Skripte %(urlEnd)s", "changing room on a RoomView is not supported": "Das Ändern eines Raumes in einer RaumAnsicht wird nicht unterstützt", @@ -607,10 +607,10 @@ "Revoke Moderator": "Moderator zurückziehen", "Search": "Suche", "Search failed": "Suche fehlgeschlagen", - "Server error": "Serverfehler", - "Server may be unavailable, overloaded, or search timed out :(": "Server ist entweder nicht verfügbar, überlastet oder die Suchezeit ist abgelaufen :(", + "Server 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": "Server ist entweder nicht verfügbar, überlastet oder etwas anderes schlug fehl", + "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", From e9cb13035b0057ed7174f4cd3473743eb5d8b934 Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 31 May 2017 15:59:30 +0000 Subject: [PATCH 101/416] Translated using Weblate (French) Currently translated at 100.0% (770 of 770 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 | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 9462a9c557..61c75512d0 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -79,7 +79,7 @@ "it": "Italian", "ja": "Japanese", "ji": "Yiddish", - "ko": "Korean (Johab)", + "ko": "Coréen", "lt": "Lithuanian", "lv": "Latvian", "mk": "Macedonian (FYROM)", @@ -100,7 +100,7 @@ "sk": "Slovak", "sl": "Slovenian", "sq": "Albanian", - "sr": "Serbian (Latin)", + "sr": "Serbe", "sv-fi": "Swedish (Finland)", "sv": "Swedish", "sx": "Sutu", @@ -149,7 +149,7 @@ "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Failed to change power level": "Failed to change power level", "Failed to delete device": "Failed to delete device", - "Failed to forget room %(errCode)s": "Echec lors de l'oublie du salon %(errCode)s", + "Failed to forget room %(errCode)s": "Échec lors de l'oubli du salon %(errCode)s", "Please Register": "Veuillez vous enregistrer", "Remove": "Supprimer", "was banned": "a été banni(e)", @@ -758,12 +758,17 @@ "Disable URL previews by default for participants in this room": "Désactiver les aperçus d'URL par défaut pour les participants de ce salon", "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "Les aperçus d'URL sont %(globalDisableUrlPreview)s par défaut pour les participants de ce salon.", "Enable URL previews for this room (affects only you)": "Activer les aperçus d'URL pour ce salon (n'affecte que vous)", - "Drop file here to upload": "Déposer le fichier ici pour téléchargement", + "Drop file here to upload": "Déposer le fichier ici pour le télécharger", " (unsupported)": " (non supporté)", "Ongoing conference call%(supportedText)s. %(joinText)s": "Appel conférence en cours%(supportedText)s. %(joinText)s", "Online": "En ligne", - "Offline": "Hors ligne", + "Offline": "Déconnecté", "Disable URL previews for this room (affects only you)": "Désactiver les aperçus d'URL pour ce salon (n'affecte que vous)", "Desktop specific": "Spécifique à la version bureau", - "Start automatically after system login": "Démarrer automatiquement après la phase d'authentification du système." + "Start automatically after system login": "Démarrer automatiquement après la phase d'authentification du système.", + "Idle": "Inactif", + "Jump to first unread message.": "Aller au premier message non-lu.", + "Options": "Options", + "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?": "Vous êtes sur le point d’accéder à un site tiers afin de pouvoir vous identifier pour utiliser %(integrationsUrl)s. Voulez vous continuer ?", + "Removed or unknown message type": "Type de message inconnu ou supprimé" } From 4bc252a16b3bc3c1c126e17dc4f4c67fcf6e1130 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 31 May 2017 17:28:08 +0100 Subject: [PATCH 102/416] delint src/Lifecycle --- src/Lifecycle.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 0bf4c575e5..bf7b25fd2b 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -232,7 +232,9 @@ function _handleRestoreFailure(e) { let msg = e.message; if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") { msg = _t( - 'You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.' + 'You need to log back in to generate end-to-end encryption keys' + + ' for this device and submit the public key to your homeserver.' + + ' This is a once off; sorry for the inconvenience.', ); _clearLocalStorage(); From c3d37c1ff9f3cb30834bb6e97c49845be9363b2f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 31 May 2017 17:28:46 +0100 Subject: [PATCH 103/416] Call MatrixClient.clearStores on logout ... to make sure that we don't have any sensitive data sitting around in the stores. --- src/Lifecycle.js | 38 +++++++++++++++++++------ src/components/structures/MatrixChat.js | 4 +-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index bf7b25fd2b..a3bec14492 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -237,7 +237,7 @@ function _handleRestoreFailure(e) { + ' This is a once off; sorry for the inconvenience.', ); - _clearLocalStorage(); + _clearStorage(); return q.reject(new Error( _t('Unable to restore previous session') + ': ' + msg, @@ -258,7 +258,7 @@ function _handleRestoreFailure(e) { return def.promise.then((success) => { if (success) { // user clicked continue. - _clearLocalStorage(); + _clearStorage(); return false; } @@ -332,6 +332,10 @@ export function setLoggedIn(credentials) { } // stop any running clients before we create a new one with these new credentials + // + // XXX: why do we have any running clients here? Maybe on sign-in after + // initial use as a guest? but what about our persistent storage? we need to + // be careful not to leak e2e data created as one user into another session. stopMatrixClient(); MatrixClientPeg.replaceUsingCreds(credentials); @@ -402,13 +406,19 @@ export function startMatrixClient() { * a session has been logged out / ended. */ export function onLoggedOut() { - _clearLocalStorage(); - stopMatrixClient(); + stopMatrixClient(true); dis.dispatch({action: 'on_logged_out'}); } -function _clearLocalStorage() { +function _clearStorage() { Analytics.logout(); + + const cli = MatrixClientPeg.get(); + if (cli) { + // TODO: *really* ought to wait for the promise to complete + cli.clearStores().done(); + } + if (!window.localStorage) { return; } @@ -425,9 +435,13 @@ function _clearLocalStorage() { } /** - * Stop all the background processes related to the current client + * Stop all the background processes related to the current client. + * + * Optionally clears persistent stores. + * + * @param {boolean} clearStores true to clear the persistent stores. */ -export function stopMatrixClient() { +export function stopMatrixClient(clearStores) { Notifier.stop(); UserActivity.stop(); Presence.stop(); @@ -436,7 +450,13 @@ export function stopMatrixClient() { if (cli) { cli.stopClient(); cli.removeAllListeners(); - cli.store.deleteAllData(); - MatrixClientPeg.unset(); } + + if (clearStores) { + // note that we have to do this *after* stopping the client, but + // *before* clearing the MatrixClientPeg. + _clearStorage(); + } + + MatrixClientPeg.unset(); } diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 64b5354eda..f53128fba9 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -292,7 +292,7 @@ module.exports = React.createClass({ }, componentWillUnmount: function() { - Lifecycle.stopMatrixClient(); + Lifecycle.stopMatrixClient(false); dis.unregister(this.dispatcherRef); UDEHandler.stopListening(); window.removeEventListener("focus", this.onFocus); @@ -364,7 +364,7 @@ module.exports = React.createClass({ // is completed in another browser, we'll be 401ed for using // a guest access token for a non-guest account. // It will be restarted in onReturnToGuestClick - Lifecycle.stopMatrixClient(); + Lifecycle.stopMatrixClient(false); this.notifyNewScreen('register'); break; From cbbed3f54443059270df75cddb6eba8332453e5e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 31 May 2017 18:31:10 +0100 Subject: [PATCH 104/416] Fixes to i18n code --- src/components/structures/login/Registration.js | 2 +- src/components/views/dialogs/SetDisplayNameDialog.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index 6dfd700d13..d9b7c46b6b 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -267,7 +267,7 @@ module.exports = React.createClass({ errMsg = _t('Passwords don\'t match.'); break; case "RegistrationForm.ERR_PASSWORD_LENGTH": - errMsg = _t('Password too short (min %(MIN_PASSWORD_LENGTH)s).', {MIN_PASSWORD_LENGTH: $MIN_PASSWORD_LENGTH}) + errMsg = _t('Password too short (min %(MIN_PASSWORD_LENGTH)s).', {MIN_PASSWORD_LENGTH: MIN_PASSWORD_LENGTH}); break; case "RegistrationForm.ERR_EMAIL_INVALID": errMsg = _t('This doesn\'t look like a valid email address.'); diff --git a/src/components/views/dialogs/SetDisplayNameDialog.js b/src/components/views/dialogs/SetDisplayNameDialog.js index 17de0aea1b..1134b1f8cd 100644 --- a/src/components/views/dialogs/SetDisplayNameDialog.js +++ b/src/components/views/dialogs/SetDisplayNameDialog.js @@ -18,6 +18,8 @@ import React from 'react'; import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; +import { _t } from '../../../languageHandler'; + /** * Prompt the user to set a display name. * From 165dfeccb9a6a2ee7743cccd9676430b507d4aed Mon Sep 17 00:00:00 2001 From: turt2live Date: Wed, 31 May 2017 11:55:25 -0600 Subject: [PATCH 105/416] Fix rare case where presence duration is undefined & i18n --- src/components/views/rooms/PresenceLabel.js | 15 +++++++-------- src/i18n/strings/en_EN.json | 4 ++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/PresenceLabel.js b/src/components/views/rooms/PresenceLabel.js index d06b46641c..b79de2b21c 100644 --- a/src/components/views/rooms/PresenceLabel.js +++ b/src/components/views/rooms/PresenceLabel.js @@ -55,17 +55,17 @@ module.exports = React.createClass({ var d = parseInt(t / (60 * 60 * 24)); if (t < 60) { if (t < 0) { - return "0s"; + return _t("for %(amount)ss", {amount: 0}); } - return s + "s"; + return _t("for %(amount)ss", {amount: s}); } if (t < 60 * 60) { - return m + "m"; + return _t("for %(amount)sm", {amount: m}); } if (t < 24 * 60 * 60) { - return h + "h"; + return _t("for %(amount)sh", {amount: h}); } - return d + "d "; + return _t("for %(amount)sd", {amount: d}); }, getPrettyPresence: function(presence) { @@ -77,9 +77,8 @@ module.exports = React.createClass({ render: function() { if (this.props.activeAgo >= 0) { - var ago = this.props.currentlyActive ? "" : "for " + (this.getDuration(this.props.activeAgo)); - // var ago = this.getDuration(this.props.activeAgo) + " ago"; - // if (this.props.currentlyActive) ago += " (now?)"; + let duration = this.getDuration(this.props.activeAgo); + let ago = this.props.currentlyActive || !duration ? "" : duration; return (
{ this.getPrettyPresence(this.props.presenceState) } { ago } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bd8b6d2367..729208dddc 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -765,6 +765,10 @@ "Drop file here to upload": "Drop file here to upload", " (unsupported)": " (unsupported)", "Ongoing conference call%(supportedText)s. %(joinText)s": "Ongoing conference call%(supportedText)s. %(joinText)s", + "for %(amount)ss": "for %(amount)ss", + "for %(amount)sm": "for %(amount)sm", + "for %(amount)sh": "for %(amount)sh", + "for %(amount)sd": "for %(amount)sd", "Online": "Online", "Idle": "Idle", "Offline": "Offline", From f2db6dd16836180001dd178e4de9a611afc3d6e4 Mon Sep 17 00:00:00 2001 From: "Iru Cai (vimacs)" Date: Wed, 31 May 2017 16:59:24 +0000 Subject: [PATCH 106/416] Translated using Weblate (Chinese (Simplified)) Currently translated at 26.8% (207 of 770 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/zh_Hans/ --- src/i18n/strings/zh_Hans.json | 53 ++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 33a44210de..91adec837f 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -154,5 +154,56 @@ "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' 超过了此主服务器的上传大小限制", "The file '%(fileName)s' failed to upload": "文件 '%(fileName)s' 上传失败", - "Guests can't use labs features. Please register.": "游客不能使用实验性功能。请注册。" + "Disable URL previews for this room (affects only you)": "在这个房间禁止URL预览(只影响你)", + "af": "南非荷兰语", + "ca": "加泰罗尼亚语", + "cs": "捷克语", + "da": "丹麦语", + "de-at": "德语(奥地利)", + "de-ch": "德语(瑞士)", + "de": "德语", + "de-lu": "德语(卢森堡)", + "el": "希腊语", + "en-au": "英语(澳大利亚)", + "en": "英语", + "zh-cn": "中文(中国)", + "zh-hk": "中文(香港)", + "zh-sg": "中文(新加坡)", + "zh-tw": "中国(台湾)", + "Add email address": "添加邮件地址", + "Add phone number": "添加电话号码", + "Advanced": "高级", + "Algorithm": "算法", + "Always show message timestamps": "总是显示消息时间戳", + "all room members": "所有聊天室成员", + "all room members, from the point they are invited": "所有聊天室成员,从他们被邀请开始", + "all room members, from the point they joined": "所有聊天室成员,从他们加入开始", + "an address": "一个地址", + "and": "和", + "%(names)s and %(lastPerson)s are typing": "%(names)s 和 %(lastPerson)s 正在打字", + "%(names)s and %(count)s others are typing": "%(names)s 和另外 %(count)s 个人正在打字", + "An email has been sent to": "一封邮件已经被发送到", + "A new password must be entered.": "一个新的密码必须被输入。", + "%(senderName)s answered the call.": "%(senderName)s 接了通话。", + "An error has occurred.": "一个错误出现了。", + "Attachment": "附件", + "Autoplay GIFs and videos": "自动播放GIF和视频", + "%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s.", + "Ban": "封禁", + "Banned users": "被封禁的用户", + "Click here": "点击这里", + "Click here to fix": "点击这里修复", + "Confirm password": "确认密码", + "Confirm your new password": "确认你的新密码", + "Continue": "继续", + "Ed25519 fingerprint": "Ed25519指纹", + "Invite new room members": "邀请新的聊天室成员", + "Join Room": "加入聊天室", + "joined": "加入了", + "%(targetName)s joined the room.": "%(targetName)s 加入了聊天室。", + "Jump to first unread message.": "跳到第一条未读消息。", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s 把 %(targetName)s 踢出了聊天室。", + "Leave room": "离开聊天室", + "Login as guest": "以游客的身份登录", + "New password": "新密码" } From 30dc5f8117eb85e493dded8d58a86249f4cd93ba Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 31 May 2017 18:15:59 +0000 Subject: [PATCH 107/416] Translated using Weblate (French) Currently translated at 100.0% (770 of 770 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 | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 61c75512d0..47ab9c7573 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -132,7 +132,7 @@ "Drop here to tag %(section)s": "Déposer ici pour marque comme %(section)s", "Ed25519 fingerprint": "Empreinte Ed25519", "Email Address": "Adresse e-mail", - "Email, name or matrix ID": "E-mail, nom or identifiant Matrix", + "Email, name or matrix ID": "E-mail, nom ou identifiant Matrix", "Emoji": "Emoticône", "Enable encryption": "Activer l'encryption", "Encrypted messages will not be visible on clients that do not yet implement encryption": "Les messages encryptés ne seront pas visibles dans les clients qui n’implémentent pas encore l’encryption", @@ -176,7 +176,7 @@ "%(targetName)s accepted an invitation.": "%(targetName)s a accepté une invitation.", "Account": "Compte", "Add email address": "Ajouter une adresse e-mail", - "Add phone number": "Ajouter un numéro de téléphone", + "Add phone number": "Ajouter un numéro", "Admin": "Admin", "Advanced": "Avancé", "Algorithm": "Algorithme", @@ -246,15 +246,15 @@ "Current password": "Mot de passe actuel", "Curve25519 identity key": "Clé d’identité Curve25519", "/ddg is not a command": "/ddg n'est pas une commande", - "Deactivate Account": "Désactiver le compte", - "Deactivate my account": "Désactiver mon compte", + "Deactivate Account": "Supprimer le compte", + "Deactivate my account": "Supprimer mon compte", "decline": "décliner", "Decrypt %(text)s": "Décrypter %(text)s", "Decryption error": "Erreur de décryptage", "Delete": "Supprimer", "demote": "rétrograder", "Deops user with given id": "Retire les privilèges d’opérateur d’un utilisateur avec un ID donné", - "Device ID": "ID de l'appareil", + "Device ID": "Identifiant de l'appareil", "Devices": "Appareils", "Devices will not yet be able to decrypt history from before they joined the room": "Les appareils ne seront pas capables de décrypter l’historique précédant leur adhésion au salon", "ml": "Malayalam", @@ -379,7 +379,7 @@ "Permissions": "Permissions", "Phone": "Numéro de téléphone", "Operation failed": "L'opération a échoué", - "Bulk Options": "Option en vrac", + "Bulk Options": "Options de masse", "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.": "Changer le mot de passe actuellement réinitialise les clés d’encryption sur tous les appareils, rendant l’historique encrypté illisible, à moins d’exporter les clés du salon en avance de phase puis de les ré-importer. Ceci sera amélioré prochainement.", "Default": "Défaut", "Email address": "Adresse e-mail", @@ -392,13 +392,13 @@ "Invalid file%(extra)s": "Fichier %(extra)s invalide", "Mute": "Couper le son", "No users have specific privileges in this room": "Aucun utilisateur n’a de privilège spécifique dans ce salon", - "olm version:": "Version de olm :", + "olm version:": "version de olm :", "Once you've followed the link it contains, click below": "Une fois que vous aurez suivi le lien qu’il contient, cliquez ci-dessous", "%(senderName)s placed a %(callType)s call.": "%(senderName)s a placé un appel %(callType)s.", - "Please check your email and click on the link it contains. Once this is done, click continue.": "Merci de vérifier vos e-mail et cliquer sur le lien quil contient. Une fois que cela est fait, cliquez sur continuer.", - "Power level must be positive integer.": "Le niveau de pouvoir doit être un entier positif.", + "Please check your email and click on the link it contains. Once this is done, click continue.": "Veuillez vérifier vos e-mails et cliquer sur le lien que vous avez reçu. Puis cliquez sur continuer.", + "Power level must be positive integer.": "Le niveau d'autorité doit être un entier positif.", "Press": "Cliquer", - "Privacy warning": "Alerte vie privée", + "Privacy warning": "Alerte de confidentialité", "Privileged Users": "Utilisateur Privilégié", "Profile": "Profil", "Reason": "Raison", @@ -425,8 +425,8 @@ "Room Colour": "Couleur du salon", "Room name (optional)": "Nom du salon (optionnel)", "Rooms": "Salons", - "Scroll to bottom of page": "Défiler jusqu’au bas de la page", - "Scroll to unread messages": "Défiler jusqu’aux messages non-lus", + "Scroll to bottom of page": "Aller en bas de la page", + "Scroll to unread messages": "Aller aux messages non-lus", "Search": "Rechercher", "Search failed": "Erreur lors de la recherche", "Searches DuckDuckGo for results": "Recherche des résultats dans DuckDuckGo", @@ -531,7 +531,7 @@ "Upload avatar": "Télécharger une photo de profil", "Upload Failed": "Erreur lors du téléchargement", "Upload Files": "Télécharger les fichiers", - "Upload file": "Télécharger le fichier", + "Upload file": "Télécharger un fichier", "Usage": "Utilisation", "Use with caution": "Utiliser avec prudence", "User ID": "Identifiant d'utilisateur", @@ -709,10 +709,10 @@ "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:": "Pour vérifier que vous pouvez faire confiance à cet appareil, merci de contacter son propriétaire par un autre moyen (par ex. en personne ou par téléphone) et demandez lui si la clé qu’il/elle voit dans ses Paramètres Utilisateur pour cet appareil correspond à la clé ci-dessous :", "Device name": "Nom de l'appareil", "Device key": "Clé de l'appareil", - "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.": "Si les clés correspondent, cliquer sur le bouton ’Vérifier’ ci-dessous. Si non, alors quelqu’un d’autre est en train d’intercepter cet appareil et vous devriez certainement cliquer sur le bouton ’Ajouter à la liste noire’ à la place.", + "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.": "Si les clés correspondent, cliquer sur le bouton ’Vérifier’ ci-dessous. Si non, alors quelqu’un d’autre est en train d’intercepter cet appareil et vous devriez certainement cliquer sur le bouton ’Blacklister' (Ajouter à la liste noire) à la place.", "In future this verification process will be more sophisticated.": "À l’avenir ce processus de vérification sera simplifié et plus sophistiqué.", "Verify device": "Vérifier cet appareil", - "I verify that the keys match": "J’ai vérifié que les clés correspondait", + "I verify that the keys match": "J’ai vérifié que les clés correspondaient", "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.": "Nous avons rencontré une erreur en essayant de rétablir votre session précédente. Si vous continuez, vous devrez vous identifier à nouveau et l’historique encrypté de vos conversations sera illisible.", "Unable to restore session": "Impossible de restaurer la 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.": "Si vous avez utilisé une version plus récente de Riot précédemment, votre session risque d’être incompatible avec cette version. Fermez cette fenêtre et retournez à la version plus récente.", @@ -724,7 +724,7 @@ "Unknown devices": "Appareils inconnus", "Unknown Address": "Adresse inconnue", "Unblacklist": "Supprimer de la liste noire", - "Blacklist": "Ajouter à la liste noire", + "Blacklist": "Blacklister", "Unverify": "Non-vérifié", "Verify...": "Vérifier...", "ex. @bob:example.com": "ex. @bob:exemple.com", @@ -764,8 +764,8 @@ "Online": "En ligne", "Offline": "Déconnecté", "Disable URL previews for this room (affects only you)": "Désactiver les aperçus d'URL pour ce salon (n'affecte que vous)", - "Desktop specific": "Spécifique à la version bureau", - "Start automatically after system login": "Démarrer automatiquement après la phase d'authentification du système.", + "Desktop specific": "Spécifique à l'application de bureau", + "Start automatically after system login": "Démarrer automatiquement après la phase d'authentification du système", "Idle": "Inactif", "Jump to first unread message.": "Aller au premier message non-lu.", "Options": "Options", From 7af2f615bd79615b0bf9cf203acf88295c4acd18 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 31 May 2017 17:18:52 +0000 Subject: [PATCH 108/416] Translated using Weblate (German) Currently translated at 99.7% (768 of 770 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 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 96dd38c78f..5850f8e3e4 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -386,7 +386,7 @@ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s hat ein Bild gesendet.", "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten.", "%(senderName)s set a profile picture.": "%(senderName)s setzte ein Profilbild.", - "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s setzte den Anzeigenamen zu %(displayName)s.", + "%(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", "To use it, just wait for autocomplete results to load and tab through them.": "Um dies zu nutzen, warte auf die Autovervollständigungsergebnisse und benutze die TAB Taste.", @@ -428,7 +428,7 @@ "to tag direct chat": "als direkten Chat markieren", "You're not in any rooms yet! Press": "Du bist noch keinem Raum beigetreten! Drücke", "click to reveal": "Klicke zum anzeigen", - "To remove other users' messages": "Um Nachrichten anderer zu verbergen", + "To remove other users' messages": "Um Nachrichten anderer Nutzer zu verbergen", "You are trying to access %(roomName)s": "Du versuchst auf %(roomName)s zuzugreifen", "af": "Afrikaans", "ar-ae": "Arabisch (U.A.E.)", @@ -580,7 +580,7 @@ "Failed to kick": "Kicken fehlgeschlagen", "Failed to mute user": "Nutzer lautlos zu stellen fehlgeschlagen", "Failed to reject invite": "Einladung abzulehnen fehlgeschlagen", - "Failed to save settings": "Einstellungen speichern fehlgeschlagen", + "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", "Failed to set display name": "Anzeigenamen zu ändern fehlgeschlagen", "Fill screen": "Fülle Bildschirm", "Guest users can't upload files. Please register to upload": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren", @@ -600,7 +600,7 @@ "New address (e.g. #foo:%(localDomain)s)": "Neue Adresse (z.B. #foo:%(localDomain)s)", "not set": "nicht gesetzt", "not specified": "nicht spezifiziert", - "No devices with registered encryption keys": "Keine Geräte mit registrierten Verschlüsselungsschlüsseln", + "No devices with registered encryption keys": "Keine Geräte mit registrierten Verschlüsselungs-Schlüsseln", "No more results": "Keine weiteren Ergebnisse", "No results": "Keine Ergebnisse", "OK": "OK", @@ -640,10 +640,10 @@ "code": "Code", "quote": "Zitat", "bullet": "Aufzählung", - "Click to unmute video": "Klicke um Video zu reaktivieren", + "Click to unmute video": "Klicken, um die Video-Stummschaltung zu deaktivieren", "Click to unmute audio": "Klicke um Ton zu reaktivieren", "Failed to load timeline position": "Laden der Position im Zeitstrahl fehlgeschlagen", - "Failed to toggle moderator status": "Umschalten des Moderatorstatus fehlgeschlagen", + "Failed to toggle moderator status": "Umschalten des Moderator-Status fehlgeschlagen", "Enable encryption": "Verschlüsselung aktivieren", "The main address for this room is": "Die Hauptadresse für diesen Raum ist", "Autoplay GIFs and videos": "GIF-Dateien und Videos automatisch abspielen", @@ -734,7 +734,7 @@ "Passphrases must match": "Passphrase muss übereinstimmen", "Passphrase must not be empty": "Passphrase darf nicht leer sein", "Export room keys": "Exportiere Raum-Schlüssel", - "Enter passphrase": "Gebe Passphrase ein", + "Enter passphrase": "Passphrase eingeben", "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 seine Dateien zu sehen", From 8cfa3bc2358e881b26eed854ad8471eafca59040 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 31 May 2017 23:49:55 +0100 Subject: [PATCH 109/416] add concept of platform handling loudNotifications (bings/pings/whatHaveYou) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.js | 3 +++ src/Notifier.js | 1 + 2 files changed, 4 insertions(+) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index ca87535a86..d0d8e0c74e 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -71,6 +71,9 @@ export default class BasePlatform { displayNotification(title: string, msg: string, avatarUrl: string, room: Object) { } + loudNotification(ev: Event, room: Object) { + } + /** * Returns a promise that resolves to a string representing * the current version of the application. diff --git a/src/Notifier.js b/src/Notifier.js index e89947e958..40a65d4106 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -250,6 +250,7 @@ const Notifier = { this._displayPopupNotification(ev, room); } if (actions.tweaks.sound && this.isAudioEnabled()) { + PlatformPeg.get().loudNotification(ev, room); this._playAudioNotification(ev, room); } } From ad0cbdd4ff7c28c7b3e1a4ac35d380ba433442cd Mon Sep 17 00:00:00 2001 From: RiotTranslate Date: Thu, 1 Jun 2017 01:39:21 +0200 Subject: [PATCH 110/416] Update from Weblate. (#981) * Translated using Weblate (French) Currently translated at 99.3% (763 of 768 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% (768 of 768 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 100.0% (770 of 770 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 26.8% (207 of 770 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (French) Currently translated at 100.0% (770 of 770 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 99.7% (768 of 770 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 +++++++++++----------- src/i18n/strings/fr.json | 52 +++++++++++++++++++--------------- src/i18n/strings/zh_Hans.json | 53 ++++++++++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 89e7f97515..5850f8e3e4 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -40,7 +40,7 @@ "Change Password": "Passwort ändern", "Searches DuckDuckGo for results": "Verwendet DuckDuckGo für Suchergebnisse", "Commands": "Kommandos", - "Emoji": "Smileys", + "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", "Return to app": "Zurück zur Anwendung", @@ -93,7 +93,7 @@ "Encryption is enabled in this room": "Verschlüsselung ist in diesem Raum aktiviert", "Encryption is not enabled in this room": "Verschlüsselung ist in diesem Raum nicht aktiviert", "ended the call.": "beendete den Anruf.", - "End-to-end encryption is in beta and may not be reliable": "Ende-zu-Ende-Verschlüsselung ist im Beta-Status und ist evtl. nicht zuverlässig", + "End-to-end encryption is in beta and may not be reliable": "Die Ende-zu-Ende-Verschlüsselung befindet sich im Beta-Stadium und ist eventuell nicht hundertprozentig zuverlässig", "Failed to send email": "Fehler beim Senden der E-Mail", "Account": "Konto", "Add phone number": "Füge Telefonnummer hinzu", @@ -360,7 +360,7 @@ "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s.", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s änderte das Thema zu \"%(topic)s\".", "/ddg is not a command": "/ddg ist kein Kommando", - "%(senderName)s ended the call.": "%(senderName)s beendete den Anruf.", + "%(senderName)s ended the call.": "%(senderName)s hat den Anruf beendet.", "Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl", "Failed to send request.": "Anfrage zu senden schlug fehl.", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s", @@ -386,7 +386,7 @@ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s hat ein Bild gesendet.", "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten.", "%(senderName)s set a profile picture.": "%(senderName)s setzte ein Profilbild.", - "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s setzte den Anzeigenamen zu %(displayName)s.", + "%(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", "To use it, just wait for autocomplete results to load and tab through them.": "Um dies zu nutzen, warte auf die Autovervollständigungsergebnisse und benutze die TAB Taste.", @@ -401,9 +401,9 @@ "Error changing language": "Fehler beim Ändern der Sprache", "Riot was unable to find the correct Data for the selected Language.": "Riot war nicht in der Lage die korrekten Daten für die ausgewählte Sprache zu finden.", "Connectivity to the server has been lost.": "Verbindung zum Server untergebrochen.", - "Sent messages will be stored until your connection has returned.": "Gesendete Nachrichten werden gespeichert bis die Verbindung wiederhergestellt wurde.", + "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": "Alles erneut senden", + "Resend all": "Alle erneut senden", "cancel all": "alles abbrechen", "now. You can also select individual messages to resend or cancel.": "jetzt. Du kannst auch einzelne Nachrichten zum erneuten Senden oder Abbrechen auswählen.", "Active call": "Aktiver Anruf", @@ -428,7 +428,7 @@ "to tag direct chat": "als direkten Chat markieren", "You're not in any rooms yet! Press": "Du bist noch keinem Raum beigetreten! Drücke", "click to reveal": "Klicke zum anzeigen", - "To remove other users' messages": "Um Nachrichten anderer zu verbergen", + "To remove other users' messages": "Um Nachrichten anderer Nutzer zu verbergen", "You are trying to access %(roomName)s": "Du versuchst auf %(roomName)s zuzugreifen", "af": "Afrikaans", "ar-ae": "Arabisch (U.A.E.)", @@ -515,7 +515,7 @@ "pt": "Portugiesisch", "rm": "Rätoromanisch", "ro-mo": "Rumänisch (Republik Moldau/Moldawien)", - "ro": "Romanian", + "ro": "Rumänisch", "ru-mo": "Russisch", "sb": "Sorbisch", "sk": "Slowakisch", @@ -559,7 +559,7 @@ "and one other...": "und ein(e) weitere(r)...", "Are you sure?": "Bist du sicher?", "Attachment": "Anhang", - "Ban": "Banne", + "Ban": "Verbannen", "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Kann nicht zum Heimserver verbinden - bitte checke eine Verbindung und stelle sicher, dass dem %(urlStart)s SSL-Zertifikat deines Heimservers %(urlEnd)s vertraut wird", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Kann nicht zum Heimserver via HTTP verbinden, wenn eine HTTPS-Url in deiner Adresszeile steht. Nutzer HTTPS oder %(urlStart)s aktiviere unsichere Skripte %(urlEnd)s", "changing room on a RoomView is not supported": "Das Ändern eines Raumes in einer RaumAnsicht wird nicht unterstützt", @@ -580,7 +580,7 @@ "Failed to kick": "Kicken fehlgeschlagen", "Failed to mute user": "Nutzer lautlos zu stellen fehlgeschlagen", "Failed to reject invite": "Einladung abzulehnen fehlgeschlagen", - "Failed to save settings": "Einstellungen speichern fehlgeschlagen", + "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", "Failed to set display name": "Anzeigenamen zu ändern fehlgeschlagen", "Fill screen": "Fülle Bildschirm", "Guest users can't upload files. Please register to upload": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren", @@ -600,17 +600,17 @@ "New address (e.g. #foo:%(localDomain)s)": "Neue Adresse (z.B. #foo:%(localDomain)s)", "not set": "nicht gesetzt", "not specified": "nicht spezifiziert", - "No devices with registered encryption keys": "Keine Geräte mit registrierten Verschlüsselungsschlüsseln", + "No devices with registered encryption keys": "Keine Geräte mit registrierten Verschlüsselungs-Schlüsseln", "No more results": "Keine weiteren Ergebnisse", "No results": "Keine Ergebnisse", "OK": "OK", "Revoke Moderator": "Moderator zurückziehen", "Search": "Suche", "Search failed": "Suche fehlgeschlagen", - "Server error": "Serverfehler", - "Server may be unavailable, overloaded, or search timed out :(": "Server ist entweder nicht verfügbar, überlastet oder die Suchezeit ist abgelaufen :(", + "Server 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": "Server ist entweder nicht verfügbar, überlastet oder etwas anderes schlug fehl", + "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", @@ -640,10 +640,10 @@ "code": "Code", "quote": "Zitat", "bullet": "Aufzählung", - "Click to unmute video": "Klicke um Video zu reaktivieren", + "Click to unmute video": "Klicken, um die Video-Stummschaltung zu deaktivieren", "Click to unmute audio": "Klicke um Ton zu reaktivieren", "Failed to load timeline position": "Laden der Position im Zeitstrahl fehlgeschlagen", - "Failed to toggle moderator status": "Umschalten des Moderatorstatus fehlgeschlagen", + "Failed to toggle moderator status": "Umschalten des Moderator-Status fehlgeschlagen", "Enable encryption": "Verschlüsselung aktivieren", "The main address for this room is": "Die Hauptadresse für diesen Raum ist", "Autoplay GIFs and videos": "GIF-Dateien und Videos automatisch abspielen", @@ -734,7 +734,7 @@ "Passphrases must match": "Passphrase muss übereinstimmen", "Passphrase must not be empty": "Passphrase darf nicht leer sein", "Export room keys": "Exportiere Raum-Schlüssel", - "Enter passphrase": "Gebe Passphrase ein", + "Enter passphrase": "Passphrase eingeben", "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 seine Dateien zu sehen", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 3d85aefbc7..47ab9c7573 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -79,7 +79,7 @@ "it": "Italian", "ja": "Japanese", "ji": "Yiddish", - "ko": "Korean (Johab)", + "ko": "Coréen", "lt": "Lithuanian", "lv": "Latvian", "mk": "Macedonian (FYROM)", @@ -100,7 +100,7 @@ "sk": "Slovak", "sl": "Slovenian", "sq": "Albanian", - "sr": "Serbian (Latin)", + "sr": "Serbe", "sv-fi": "Swedish (Finland)", "sv": "Swedish", "sx": "Sutu", @@ -119,7 +119,7 @@ "zh-sg": "Chinese (Singapore)", "zh-tw": "Chinese (Taiwan)", "zu": "Zulu", - "anyone": "anyone", + "anyone": "n'importe qui", "Direct Chat": "Conversation Directe", "Direct chats": "Conversations directes", "Disable inline URL previews by default": "Désactiver l’aperçu des URLs", @@ -132,7 +132,7 @@ "Drop here to tag %(section)s": "Déposer ici pour marque comme %(section)s", "Ed25519 fingerprint": "Empreinte Ed25519", "Email Address": "Adresse e-mail", - "Email, name or matrix ID": "E-mail, nom or identifiant Matrix", + "Email, name or matrix ID": "E-mail, nom ou identifiant Matrix", "Emoji": "Emoticône", "Enable encryption": "Activer l'encryption", "Encrypted messages will not be visible on clients that do not yet implement encryption": "Les messages encryptés ne seront pas visibles dans les clients qui n’implémentent pas encore l’encryption", @@ -149,7 +149,7 @@ "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Failed to change power level": "Failed to change power level", "Failed to delete device": "Failed to delete device", - "Failed to forget room %(errCode)s": "Echec lors de l'oublie du salon %(errCode)s", + "Failed to forget room %(errCode)s": "Échec lors de l'oubli du salon %(errCode)s", "Please Register": "Veuillez vous enregistrer", "Remove": "Supprimer", "was banned": "a été banni(e)", @@ -176,7 +176,7 @@ "%(targetName)s accepted an invitation.": "%(targetName)s a accepté une invitation.", "Account": "Compte", "Add email address": "Ajouter une adresse e-mail", - "Add phone number": "Ajouter un numéro de téléphone", + "Add phone number": "Ajouter un numéro", "Admin": "Admin", "Advanced": "Avancé", "Algorithm": "Algorithme", @@ -246,15 +246,15 @@ "Current password": "Mot de passe actuel", "Curve25519 identity key": "Clé d’identité Curve25519", "/ddg is not a command": "/ddg n'est pas une commande", - "Deactivate Account": "Désactiver le compte", - "Deactivate my account": "Désactiver mon compte", + "Deactivate Account": "Supprimer le compte", + "Deactivate my account": "Supprimer mon compte", "decline": "décliner", "Decrypt %(text)s": "Décrypter %(text)s", "Decryption error": "Erreur de décryptage", "Delete": "Supprimer", "demote": "rétrograder", "Deops user with given id": "Retire les privilèges d’opérateur d’un utilisateur avec un ID donné", - "Device ID": "ID de l'appareil", + "Device ID": "Identifiant de l'appareil", "Devices": "Appareils", "Devices will not yet be able to decrypt history from before they joined the room": "Les appareils ne seront pas capables de décrypter l’historique précédant leur adhésion au salon", "ml": "Malayalam", @@ -379,7 +379,7 @@ "Permissions": "Permissions", "Phone": "Numéro de téléphone", "Operation failed": "L'opération a échoué", - "Bulk Options": "Option en vrac", + "Bulk Options": "Options de masse", "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.": "Changer le mot de passe actuellement réinitialise les clés d’encryption sur tous les appareils, rendant l’historique encrypté illisible, à moins d’exporter les clés du salon en avance de phase puis de les ré-importer. Ceci sera amélioré prochainement.", "Default": "Défaut", "Email address": "Adresse e-mail", @@ -392,13 +392,13 @@ "Invalid file%(extra)s": "Fichier %(extra)s invalide", "Mute": "Couper le son", "No users have specific privileges in this room": "Aucun utilisateur n’a de privilège spécifique dans ce salon", - "olm version:": "Version de olm :", + "olm version:": "version de olm :", "Once you've followed the link it contains, click below": "Une fois que vous aurez suivi le lien qu’il contient, cliquez ci-dessous", "%(senderName)s placed a %(callType)s call.": "%(senderName)s a placé un appel %(callType)s.", - "Please check your email and click on the link it contains. Once this is done, click continue.": "Merci de vérifier vos e-mail et cliquer sur le lien quil contient. Une fois que cela est fait, cliquez sur continuer.", - "Power level must be positive integer.": "Le niveau de pouvoir doit être un entier positif.", + "Please check your email and click on the link it contains. Once this is done, click continue.": "Veuillez vérifier vos e-mails et cliquer sur le lien que vous avez reçu. Puis cliquez sur continuer.", + "Power level must be positive integer.": "Le niveau d'autorité doit être un entier positif.", "Press": "Cliquer", - "Privacy warning": "Alerte vie privée", + "Privacy warning": "Alerte de confidentialité", "Privileged Users": "Utilisateur Privilégié", "Profile": "Profil", "Reason": "Raison", @@ -425,8 +425,8 @@ "Room Colour": "Couleur du salon", "Room name (optional)": "Nom du salon (optionnel)", "Rooms": "Salons", - "Scroll to bottom of page": "Défiler jusqu’au bas de la page", - "Scroll to unread messages": "Défiler jusqu’aux messages non-lus", + "Scroll to bottom of page": "Aller en bas de la page", + "Scroll to unread messages": "Aller aux messages non-lus", "Search": "Rechercher", "Search failed": "Erreur lors de la recherche", "Searches DuckDuckGo for results": "Recherche des résultats dans DuckDuckGo", @@ -531,7 +531,7 @@ "Upload avatar": "Télécharger une photo de profil", "Upload Failed": "Erreur lors du téléchargement", "Upload Files": "Télécharger les fichiers", - "Upload file": "Télécharger le fichier", + "Upload file": "Télécharger un fichier", "Usage": "Utilisation", "Use with caution": "Utiliser avec prudence", "User ID": "Identifiant d'utilisateur", @@ -709,10 +709,10 @@ "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:": "Pour vérifier que vous pouvez faire confiance à cet appareil, merci de contacter son propriétaire par un autre moyen (par ex. en personne ou par téléphone) et demandez lui si la clé qu’il/elle voit dans ses Paramètres Utilisateur pour cet appareil correspond à la clé ci-dessous :", "Device name": "Nom de l'appareil", "Device key": "Clé de l'appareil", - "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.": "Si les clés correspondent, cliquer sur le bouton ’Vérifier’ ci-dessous. Si non, alors quelqu’un d’autre est en train d’intercepter cet appareil et vous devriez certainement cliquer sur le bouton ’Ajouter à la liste noire’ à la place.", + "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.": "Si les clés correspondent, cliquer sur le bouton ’Vérifier’ ci-dessous. Si non, alors quelqu’un d’autre est en train d’intercepter cet appareil et vous devriez certainement cliquer sur le bouton ’Blacklister' (Ajouter à la liste noire) à la place.", "In future this verification process will be more sophisticated.": "À l’avenir ce processus de vérification sera simplifié et plus sophistiqué.", "Verify device": "Vérifier cet appareil", - "I verify that the keys match": "J’ai vérifié que les clés correspondait", + "I verify that the keys match": "J’ai vérifié que les clés correspondaient", "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.": "Nous avons rencontré une erreur en essayant de rétablir votre session précédente. Si vous continuez, vous devrez vous identifier à nouveau et l’historique encrypté de vos conversations sera illisible.", "Unable to restore session": "Impossible de restaurer la 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.": "Si vous avez utilisé une version plus récente de Riot précédemment, votre session risque d’être incompatible avec cette version. Fermez cette fenêtre et retournez à la version plus récente.", @@ -724,7 +724,7 @@ "Unknown devices": "Appareils inconnus", "Unknown Address": "Adresse inconnue", "Unblacklist": "Supprimer de la liste noire", - "Blacklist": "Ajouter à la liste noire", + "Blacklist": "Blacklister", "Unverify": "Non-vérifié", "Verify...": "Vérifier...", "ex. @bob:example.com": "ex. @bob:exemple.com", @@ -758,11 +758,17 @@ "Disable URL previews by default for participants in this room": "Désactiver les aperçus d'URL par défaut pour les participants de ce salon", "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "Les aperçus d'URL sont %(globalDisableUrlPreview)s par défaut pour les participants de ce salon.", "Enable URL previews for this room (affects only you)": "Activer les aperçus d'URL pour ce salon (n'affecte que vous)", - "Drop file here to upload": "Déposer le fichier ici pour téléchargement", + "Drop file here to upload": "Déposer le fichier ici pour le télécharger", " (unsupported)": " (non supporté)", "Ongoing conference call%(supportedText)s. %(joinText)s": "Appel conférence en cours%(supportedText)s. %(joinText)s", "Online": "En ligne", - "Offline": "Hors ligne", + "Offline": "Déconnecté", "Disable URL previews for this room (affects only you)": "Désactiver les aperçus d'URL pour ce salon (n'affecte que vous)", - "Desktop specific": "Spécifique à la version bureau" + "Desktop specific": "Spécifique à l'application de bureau", + "Start automatically after system login": "Démarrer automatiquement après la phase d'authentification du système", + "Idle": "Inactif", + "Jump to first unread message.": "Aller au premier message non-lu.", + "Options": "Options", + "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?": "Vous êtes sur le point d’accéder à un site tiers afin de pouvoir vous identifier pour utiliser %(integrationsUrl)s. Voulez vous continuer ?", + "Removed or unknown message type": "Type de message inconnu ou supprimé" } diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 33a44210de..91adec837f 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -154,5 +154,56 @@ "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' 超过了此主服务器的上传大小限制", "The file '%(fileName)s' failed to upload": "文件 '%(fileName)s' 上传失败", - "Guests can't use labs features. Please register.": "游客不能使用实验性功能。请注册。" + "Disable URL previews for this room (affects only you)": "在这个房间禁止URL预览(只影响你)", + "af": "南非荷兰语", + "ca": "加泰罗尼亚语", + "cs": "捷克语", + "da": "丹麦语", + "de-at": "德语(奥地利)", + "de-ch": "德语(瑞士)", + "de": "德语", + "de-lu": "德语(卢森堡)", + "el": "希腊语", + "en-au": "英语(澳大利亚)", + "en": "英语", + "zh-cn": "中文(中国)", + "zh-hk": "中文(香港)", + "zh-sg": "中文(新加坡)", + "zh-tw": "中国(台湾)", + "Add email address": "添加邮件地址", + "Add phone number": "添加电话号码", + "Advanced": "高级", + "Algorithm": "算法", + "Always show message timestamps": "总是显示消息时间戳", + "all room members": "所有聊天室成员", + "all room members, from the point they are invited": "所有聊天室成员,从他们被邀请开始", + "all room members, from the point they joined": "所有聊天室成员,从他们加入开始", + "an address": "一个地址", + "and": "和", + "%(names)s and %(lastPerson)s are typing": "%(names)s 和 %(lastPerson)s 正在打字", + "%(names)s and %(count)s others are typing": "%(names)s 和另外 %(count)s 个人正在打字", + "An email has been sent to": "一封邮件已经被发送到", + "A new password must be entered.": "一个新的密码必须被输入。", + "%(senderName)s answered the call.": "%(senderName)s 接了通话。", + "An error has occurred.": "一个错误出现了。", + "Attachment": "附件", + "Autoplay GIFs and videos": "自动播放GIF和视频", + "%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s.", + "Ban": "封禁", + "Banned users": "被封禁的用户", + "Click here": "点击这里", + "Click here to fix": "点击这里修复", + "Confirm password": "确认密码", + "Confirm your new password": "确认你的新密码", + "Continue": "继续", + "Ed25519 fingerprint": "Ed25519指纹", + "Invite new room members": "邀请新的聊天室成员", + "Join Room": "加入聊天室", + "joined": "加入了", + "%(targetName)s joined the room.": "%(targetName)s 加入了聊天室。", + "Jump to first unread message.": "跳到第一条未读消息。", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s 把 %(targetName)s 踢出了聊天室。", + "Leave room": "离开聊天室", + "Login as guest": "以游客的身份登录", + "New password": "新密码" } From b4284cf0004458efe7d5bd18d7dafc0fd9f7752b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 01:17:39 +0100 Subject: [PATCH 111/416] fix up Can't connect to HS i18n with _tJsx --- scripts/check-i18n.pl | 4 ++-- scripts/fix-i18n.pl | 4 ++++ src/components/structures/login/Login.js | 15 +++++++++------ src/i18n/strings/da.json | 1 - src/i18n/strings/de_DE.json | 6 ++---- src/i18n/strings/en_EN.json | 13 ++++++++----- src/i18n/strings/es.json | 4 ++-- src/i18n/strings/fr.json | 4 ++-- src/i18n/strings/nl.json | 4 ++-- src/i18n/strings/pt_BR.json | 4 ++-- src/i18n/strings/ru.json | 4 ++-- src/i18n/strings/zh_Hant.json | 4 ++-- 12 files changed, 37 insertions(+), 30 deletions(-) diff --git a/scripts/check-i18n.pl b/scripts/check-i18n.pl index 0ace98d0fc..fa11bc5292 100755 --- a/scripts/check-i18n.pl +++ b/scripts/check-i18n.pl @@ -178,12 +178,12 @@ sub read_src_strings { $src =~ s/"\s*\+\s*"//g; $file =~ s/^.*\/src/src/; - while ($src =~ /_t\(\s*'(.*?[^\\])'/sg) { + while ($src =~ /_t(?:Jsx)?\(\s*'(.*?[^\\])'/sg) { my $s = $1; $s =~ s/\\'/'/g; push @$strings, [$s, $file]; } - while ($src =~ /_t\(\s*"(.*?[^\\])"/sg) { + while ($src =~ /_t(?:Jsx)?\(\s*"(.*?[^\\])"/sg) { push @$strings, [$1, $file]; } } diff --git a/scripts/fix-i18n.pl b/scripts/fix-i18n.pl index d4ae3dfb49..9a12c9d909 100755 --- a/scripts/fix-i18n.pl +++ b/scripts/fix-i18n.pl @@ -48,6 +48,10 @@ Guests can't use labs features. Please register. A new password must be entered. Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved. Guests cannot join this room even if explicitly invited. +Guest users can't invite users. Please register to invite. +This room is inaccessible to guests. You may be able to join if you register. +delete the alias. +remove %(name)s from the directory. EOT )]; } diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index eb4e559237..59d8c74022 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -222,17 +222,20 @@ module.exports = React.createClass({ (this.state.enteredHomeserverUrl.startsWith("http:") || !this.state.enteredHomeserverUrl.startsWith("http"))) { - const urlStart = ; - const urlEnd = ; errorText = - { _t('Can\'t connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s', {urlStart: urlStart, urlEnd: urlEnd})} + { _tJsx("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " + + "Either use HTTPS or enable unsafe scripts.", + /(.*?)<\/a>/, + (sub) => { return { sub }; } + )} ; } else { - const urlStart = ; - const urlEnd = ; errorText = - { _t('Can\'t connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver\'s SSL certificate %(urlEnd)s is trusted', {urlStart: urlStart, urlEnd: urlEnd})} + { _tJsx("Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.", + /(.*?)<\/a>/, + (sub) => { return { sub }; } + )} ; } } diff --git a/src/i18n/strings/da.json b/src/i18n/strings/da.json index 92665ac8d0..8ac311d315 100644 --- a/src/i18n/strings/da.json +++ b/src/i18n/strings/da.json @@ -40,7 +40,6 @@ "Searches DuckDuckGo for results": "Søger DuckDuckGo for resultater", "Commands": "kommandoer", "Emoji": "Emoji", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar.": "Kan ikke oprette forbindelse til hjemmeserver via HTTP, når en HTTPS-URL er i din browserbjælke.", "Sorry, this homeserver is using a login which is not recognised ": "Beklager, denne homeerver bruger et login, der ikke kan genkendes ", "Login as guest": "Log ind som gæst", "Return to app": "Tilbage til app", diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 5850f8e3e4..aafc06faec 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -228,7 +228,6 @@ "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", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar": "Verbindung zum Homeserver ist über HTTP nicht möglich, wenn HTTPS in deiner Browserleiste steht", "Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen", "Disable inline URL previews by default": "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 +270,6 @@ "Who would you like to add to this room?": "Wen möchtest du zu diesem Raum hinzufügen?", "Who would you like to communicate with?": "Mit wem möchtest du kommunizieren?", "Would you like to": "Möchtest du", - "You are trying to access": "Du möchtest Zugang zu %(sth)s bekommen", "You do not have permission to post to this room": "Du hast keine Berechtigung an diesen Raum etwas zu senden", "You have been invited to join this room by %(inviterName)s": "Du wurdest in diesen Raum eingeladen von %(inviterName)s", "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Du wurdest von allen Geräten ausgeloggt und wirst keine Push-Benachrichtigungen mehr bekommen. Um Benachrichtigungen zu reaktivieren melde dich auf jedem Gerät neu an", @@ -560,8 +558,8 @@ "Are you sure?": "Bist du sicher?", "Attachment": "Anhang", "Ban": "Verbannen", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Kann nicht zum Heimserver verbinden - bitte checke eine Verbindung und stelle sicher, dass dem %(urlStart)s SSL-Zertifikat deines Heimservers %(urlEnd)s vertraut wird", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Kann nicht zum Heimserver via HTTP verbinden, wenn eine HTTPS-Url in deiner Adresszeile steht. Nutzer HTTPS oder %(urlStart)s aktiviere unsichere Skripte %(urlEnd)s", + "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 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", "Click to mute video": "Klicken, um das Video stummzuschalten", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 729208dddc..ab2286b57b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -166,8 +166,8 @@ "Bug Report": "Bug Report", "Bulk Options": "Bulk Options", "Call Timeout": "Call Timeout", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s", + "Can't 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 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", "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.", @@ -220,6 +220,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", + "disabled": "disabled", "Disable inline URL previews by default": "Disable inline URL previews by default", "Disinvite": "Disinvite", "Display name": "Display name", @@ -234,6 +235,7 @@ "Email, name or matrix ID": "Email, name or matrix ID", "Emoji": "Emoji", "Enable encryption": "Enable encryption", + "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", "%(senderName)s ended the call.": "%(senderName)s ended the call.", @@ -584,6 +586,7 @@ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", "Set a display name:": "Set a display name:", + "Set a Display Name": "Set a Display Name", "Upload an avatar:": "Upload an avatar:", "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", "Missing password.": "Missing password.", @@ -682,7 +685,6 @@ "Opt out of analytics": "Opt out of analytics", "Options": "Options", "Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.", - "Please select the destination room for this message": "Please select the destination room for this message", "Passphrases must match": "Passphrases must match", "Passphrase must not be empty": "Passphrase must not be empty", "Export room keys": "Export room keys", @@ -690,11 +692,12 @@ "Confirm passphrase": "Confirm passphrase", "Import room keys": "Import room keys", "File to import": "File to import", - "Enter passphrase": "Enter passphrase", "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.": "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.", "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.": "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.": "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.", + "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.", diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 818fbf0c39..849a685667 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -166,8 +166,8 @@ "Bug Report": "Reporte de error", "Bulk Options": "Opciones masivas", "Call Timeout": "Tiempo de espera de la llamada", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "No se puede conectar con el servidor - Por favor verifique su conexión y asegúrese de que su %(urlStart)s certificado SSL del servidor %(urlEnd)s sea confiable", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "No se puede conectar al servidor via HTTP, cuando es necesario un enlace HTTPS en la barra de direcciones de tu navegador. Ya sea usando HTTPS o %(urlStart)s habilitando los scripts inseguros %(urlEnd)s", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "No se puede conectar con el servidor - Por favor verifique su conexión y asegúrese de que su certificado SSL del servidor sea confiable.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "No se puede conectar al servidor via HTTP, cuando es necesario un enlace HTTPS en la barra de direcciones de tu navegador. Ya sea usando HTTPS o habilitando los scripts inseguros.", "Can't load user settings": "No se puede cargar las configuraciones del usuario", "Change Password": "Cambiar clave", "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s ha cambiado su nombre de %(oldDisplayName)s a %(displayName)s.", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 47ab9c7573..e518f80924 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -209,8 +209,8 @@ "Blacklisted": "Sur liste noire", "Bug Report": "Rapport d'erreur", "Call Timeout": "Délai d’appel expiré", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Connexion au Home Server impossible - merci de vérifier votre connectivité et que le %(urlStart)s certificat SSL de votre Home Server %(urlEnd)s est de confiance", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Impossible de se connecter au homeserver en HTTP si l'URL dans la barre de votre explorateur est en HTTPS. Utilisez HTTPS ou %(urlStart)s activez le support des scripts non-vérifiés %(urlEnd)s", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Connexion au Home Server impossible - merci de vérifier votre connectivité et que le certificat SSL de votre Home Server est de confiance.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Impossible de se connecter au homeserver en HTTP si l'URL dans la barre de votre explorateur est en HTTPS. Utilisez HTTPS ou activez le support des scripts non-vérifiés.", "Can't load user settings": "Impossible de charger les paramètres utilisateur", "Change Password": "Changer le mot de passe", "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s a changé son nom d’affichage de %(oldDisplayName)s en %(displayName)s.", diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 809fe6c0d6..668fd2ba32 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -167,8 +167,8 @@ "Bug Report": "Bug report", "Bulk Options": "Bulk opties", "Call Timeout": "Gesprek time-out", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Kan niet met de homeserver verbinden - controleer alsjeblieft je verbinding en wees zeker dat je %(urlStart)s homeserver's SSL certificaat %(urlEnd)s vertrouwd wordt", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Kan niet met de homeserver verbinden via HTTP wanneer er een HTTPS URL in je browser balk staat. Gebruik HTTPS of %(urlStart)s activeer onveilige scripts %(urlEnd)s", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Kan niet met de homeserver verbinden - controleer alsjeblieft je verbinding en wees zeker dat je homeserver's SSL certificaat vertrouwd wordt.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kan niet met de homeserver verbinden via HTTP wanneer er een HTTPS URL in je browser balk staat. Gebruik HTTPS of activeer onveilige scripts.", "Can't load user settings": "Kan de gebruiker instellingen niet laden", "Change Password": "Wachtwoord veranderen", "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s heeft zijn of haar weergave naam veranderd van %(oldDisplayName)s naar %(displayName)s.", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 7e4faf99a7..eb75bd4433 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -567,8 +567,8 @@ "sx": "Sutu", "sz": "Sami (Lappish)", "ve": "Venda", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Não consigo conectar ao servidor padrão - favor checar sua conexão à internet e verificar se o certificado SSL do seu %(urlStart)s servidor padrão %(urlEnd)s é confiável", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Não consigo conectar ao servidor padrão através de HTTP quando uma URL HTTPS está na barra de endereços do seu navegador. Use HTTPS ou então %(urlStart)s habilite scripts não seguros no seu navegador %(urlEnd)s", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Não consigo conectar ao servidor padrão - favor checar sua conexão à internet e verificar se o certificado SSL do seu servidor padrão é confiável.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Não consigo conectar ao servidor padrão através de HTTP quando uma URL HTTPS está na barra de endereços do seu navegador. Use HTTPS ou então habilite scripts não seguros no seu navegador.", "Change Password": "Alterar senha", "changing room on a RoomView is not supported": "mudar a sala em uma 'RoomView' não é permitido", "Click to mute audio": "Clique para colocar o áudio no mudo", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 9ed540a198..1ae4719402 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -452,7 +452,7 @@ "and %(overflowCount)s others...": "и %(overflowCount)s других...", "Are you sure?": "Вы уверены?", "Autoplay GIFs and videos": "Проигрывать GIF и видео автоматически", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Невозможно соединиться с домашним сервером - проверьте своё соединение и убедитесь, что %(urlStart)s SSL-сертификат вашего домашнего сервера %(urlEnd)s включён в доверяемые", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Невозможно соединиться с домашним сервером - проверьте своё соединение и убедитесь, что SSL-сертификат вашего домашнего сервера включён в доверяемые.", "changing room on a RoomView is not supported": "изменение комнаты в RoomView не поддерживается", "Click to mute audio": "Выключить звук", "Click to mute video": "Выключить звук у видео", @@ -651,5 +651,5 @@ "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)sизменил своё изображение %(repeats)s раз", "%(severalUsers)schanged their avatar": "%(severalUsers)sизменили своё изображение", "%(oneUser)schanged their avatar": "%(oneUser)sизменил своё изображение", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Не возможно подключиться к серверу через HTTP, когда в строке браузера HTTPS. Используйте HTTPS или %(urlStart) включив небезопасные скрипты %(urlEnd)" + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Не возможно подключиться к серверу через HTTP, когда в строке браузера HTTPS. Используйте HTTPS или включив небезопасные скрипты." } diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index d719b56ecc..f98bf452af 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -16,8 +16,8 @@ "Blacklisted": "已列入黑名單", "Bug Report": "錯誤報告", "Call Timeout": "通話超時", - "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "無法連結主伺服器 - 請檢查網路狀況並確保您的 %(urlStart)s 主伺服器 SSL 證書 %(urlEnd)s 得到信任", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "當瀏覽器網址列里有 HTTPS URL 時,不能使用 HTTP 連結主伺服器。請採用 HTTPS 或者%(urlStart)s 允許不安全的腳本 %(urlEnd)s", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "無法連結主伺服器 - 請檢查網路狀況並確保您的 主伺服器 SSL 證書 得到信任", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "當瀏覽器網址列里有 HTTPS URL 時,不能使用 HTTP 連結主伺服器。請採用 HTTPS 或者 允許不安全的腳本", "Can't load user settings": "無法載入使用者設定", "Change Password": "變更密碼", "%(targetName)s left the room.": "%(targetName)s 離開了聊天室。" From 5e3af22c5d147893d9dde44ac9cb98ec3182fadf Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 01:18:46 +0100 Subject: [PATCH 112/416] fix broken i18n variable --- 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 eb75bd4433..3846d5ba03 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -742,7 +742,7 @@ "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "O arquivo exportado será protegido com uma senha. Você deverá inserir a senha aqui para poder descriptografar o arquivo futuramente.", "You must join the room to see its files": "Você precisa ingressar na sala para ver seus arquivos", "Server may be unavailable, overloaded, or you hit a bug.": "O servidor pode estar indisponível ou sobrecarregado, ou então você encontrou uma falha no sistema.", - "Reject all %(invitedRooms)s invites": "Rejeitar todos os %(invitedRoom)s convites", + "Reject all %(invitedRooms)s invites": "Rejeitar todos os %(invitedRooms)s convites", "Start new Chat": "Iniciar nova conversa", "Guest users can't invite users. Please register.": "Visitantes não podem convidar usuárias(os) registradas(os). Favor registrar.", "Failed to invite": "Falha ao enviar o convite", From 3d91b3163747a9b8b291055af3aab07780bfb04d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 01:29:07 +0100 Subject: [PATCH 113/416] i18n invited, and delete stale warning --- src/components/views/rooms/MemberList.js | 8 +------- src/i18n/strings/en_EN.json | 1 + src/i18n/strings/fr.json | 1 + 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 20a0851bd4..63737d5fad 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -28,12 +28,6 @@ var CallHandler = require("../../../CallHandler"); var Invite = require("../../../Invite"); var INITIAL_LOAD_NUM_MEMBERS = 30; -var SHARE_HISTORY_WARNING = - - Newly invited users will see the history of this room.
- If you'd prefer invited users not to see messages that were sent before they joined,
- turn off, 'Share message history with new users' in the settings for this room. -
; module.exports = React.createClass({ displayName: 'MemberList', @@ -355,7 +349,7 @@ module.exports = React.createClass({ if (invitedMemberTiles.length > 0) { invitedSection = (
-

Invited

+

{ _t("Invited") }

{invitedMemberTiles}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ab2286b57b..15aa3916bc 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -305,6 +305,7 @@ "Invalid file%(extra)s": "Invalid file%(extra)s", "%(senderName)s invited %(targetName)s.": "%(senderName)s invited %(targetName)s.", "Invite new room members": "Invite new room members", + "Invited": "Invited", "Invites": "Invites", "Invites user with given id to current room": "Invites user with given id to current room", "is a": "is a", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index e518f80924..0f54913030 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -311,6 +311,7 @@ "Invalid Email Address": "Adresse e-mail invalide", "%(senderName)s invited %(targetName)s.": "%(senderName)s a invité %(targetName)s.", "Invite new room members": "Inviter de nouveaux membres", + "Invited": "Invités", "Invites": "Invitations", "Invites user with given id to current room": "Inviter l’utilisateur avec un ID donné dans le salon actuel", "is a": "est un", From d0215983805b88525dc84b5ccc18a6ada07ef5d7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 02:04:43 +0100 Subject: [PATCH 114/416] fix typo --- src/languageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index e447a5678b..1c3acab082 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -107,7 +107,7 @@ export function _tJsx(jsxText, patterns, subs) { } // Allow overriding the text displayed when no translation exists -// Currently only use din unit tests to avoid having to load +// Currently only used in unit tests to avoid having to load // the translations in riot-web export function setMissingEntryGenerator(f) { counterpart.setMissingEntryGenerator(f); From b1475cb3090d5adb2aa5d0f1993d20f9337adf71 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 02:04:46 +0100 Subject: [PATCH 115/416] bump js-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f1d4fb6e6..a6076a56d2 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "isomorphic-fetch": "^2.2.1", "linkifyjs": "^2.1.3", "lodash": "^4.13.1", - "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "0.7.9", "optimist": "^0.6.1", "q": "^1.4.1", "react": "^15.4.0", From b63adc9b105605d87a37ddfeaccbf0bae123cc36 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 02:15:48 +0100 Subject: [PATCH 116/416] Prepare changelog for v0.9.0-rc.1 --- CHANGELOG.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b9ecdb325..23098c4749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,115 @@ +Changes in [0.9.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.9.0-rc.1) (2017-06-01) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.9...v0.9.0-rc.1) + + * Fix rare case where presence duration is undefined + [\#982](https://github.com/matrix-org/matrix-react-sdk/pull/982) + * add concept of platform handling loudNotifications (bings/pings/whatHaveYou) + [\#985](https://github.com/matrix-org/matrix-react-sdk/pull/985) + * Fixes to i18n code + [\#984](https://github.com/matrix-org/matrix-react-sdk/pull/984) + * Update from Weblate. + [\#978](https://github.com/matrix-org/matrix-react-sdk/pull/978) + * Add partial support for RTL languages + [\#955](https://github.com/matrix-org/matrix-react-sdk/pull/955) + * Added two strings to translate + [\#975](https://github.com/matrix-org/matrix-react-sdk/pull/975) + * Update from Weblate. + [\#976](https://github.com/matrix-org/matrix-react-sdk/pull/976) + * Update from Weblate. + [\#974](https://github.com/matrix-org/matrix-react-sdk/pull/974) + * Initial Electron Settings - for Auto Launch + [\#920](https://github.com/matrix-org/matrix-react-sdk/pull/920) + * Fix missing string in the room settings + [\#973](https://github.com/matrix-org/matrix-react-sdk/pull/973) + * fix error in i18n string + [\#972](https://github.com/matrix-org/matrix-react-sdk/pull/972) + * Update from Weblate. + [\#970](https://github.com/matrix-org/matrix-react-sdk/pull/970) + * Support 12hr time in full date + [\#971](https://github.com/matrix-org/matrix-react-sdk/pull/971) + * Add _tJsx() + [\#968](https://github.com/matrix-org/matrix-react-sdk/pull/968) + * Update from Weblate. + [\#966](https://github.com/matrix-org/matrix-react-sdk/pull/966) + * Remove space between time and AM/PM + [\#969](https://github.com/matrix-org/matrix-react-sdk/pull/969) + * Piwik Analytics + [\#948](https://github.com/matrix-org/matrix-react-sdk/pull/948) + * Update from Weblate. + [\#965](https://github.com/matrix-org/matrix-react-sdk/pull/965) + * Improve ChatInviteDialog perf by ditching fuse, using indexOf and + lastActiveTs() + [\#960](https://github.com/matrix-org/matrix-react-sdk/pull/960) + * Say "X removed the room name" instead of showing nothing + [\#958](https://github.com/matrix-org/matrix-react-sdk/pull/958) + * roomview/roomheader fixes + [\#959](https://github.com/matrix-org/matrix-react-sdk/pull/959) + * Update from Weblate. + [\#953](https://github.com/matrix-org/matrix-react-sdk/pull/953) + * fix i18n in a situation where navigator.languages=[] + [\#956](https://github.com/matrix-org/matrix-react-sdk/pull/956) + * `t_` -> `_t` fix typo + [\#957](https://github.com/matrix-org/matrix-react-sdk/pull/957) + * Change redact -> remove for clarity + [\#831](https://github.com/matrix-org/matrix-react-sdk/pull/831) + * Update from Weblate. + [\#950](https://github.com/matrix-org/matrix-react-sdk/pull/950) + * fix mis-linting - missed it in code review :( + [\#952](https://github.com/matrix-org/matrix-react-sdk/pull/952) + * i18n fixes + [\#951](https://github.com/matrix-org/matrix-react-sdk/pull/951) + * Message Forwarding + [\#812](https://github.com/matrix-org/matrix-react-sdk/pull/812) + * don't focus_composer on window focus + [\#944](https://github.com/matrix-org/matrix-react-sdk/pull/944) + * Fix vector-im/riot-web#4042 + [\#947](https://github.com/matrix-org/matrix-react-sdk/pull/947) + * import _t, drop two unused imports + [\#946](https://github.com/matrix-org/matrix-react-sdk/pull/946) + * Fix punctuation in TextForEvent to be i18n'd consistently + [\#945](https://github.com/matrix-org/matrix-react-sdk/pull/945) + * actually wire up alwaysShowTimestamps + [\#940](https://github.com/matrix-org/matrix-react-sdk/pull/940) + * Update from Weblate. + [\#943](https://github.com/matrix-org/matrix-react-sdk/pull/943) + * Update from Weblate. + [\#942](https://github.com/matrix-org/matrix-react-sdk/pull/942) + * Update from Weblate. + [\#941](https://github.com/matrix-org/matrix-react-sdk/pull/941) + * Update from Weblate. + [\#938](https://github.com/matrix-org/matrix-react-sdk/pull/938) + * Fix PM being AM + [\#939](https://github.com/matrix-org/matrix-react-sdk/pull/939) + * pass call state through dispatcher, for poor electron + [\#918](https://github.com/matrix-org/matrix-react-sdk/pull/918) + * Translations! + [\#934](https://github.com/matrix-org/matrix-react-sdk/pull/934) + * Remove suffix and prefix from login input username + [\#906](https://github.com/matrix-org/matrix-react-sdk/pull/906) + * Kierangould/12hourtimestamp + [\#903](https://github.com/matrix-org/matrix-react-sdk/pull/903) + * Don't include src in the test resolve root + [\#931](https://github.com/matrix-org/matrix-react-sdk/pull/931) + * Make the linked versions open a new tab, turt2live complained :P + [\#910](https://github.com/matrix-org/matrix-react-sdk/pull/910) + * Fix lint errors in SlashCommands + [\#919](https://github.com/matrix-org/matrix-react-sdk/pull/919) + * autoFocus input box + [\#911](https://github.com/matrix-org/matrix-react-sdk/pull/911) + * Make travis test against riot-web new-guest-access + [\#917](https://github.com/matrix-org/matrix-react-sdk/pull/917) + * Add right-branch logic to travis test script + [\#916](https://github.com/matrix-org/matrix-react-sdk/pull/916) + * Group e2e keys into blocks of 4 characters + [\#914](https://github.com/matrix-org/matrix-react-sdk/pull/914) + * Factor out DeviceVerifyDialog + [\#913](https://github.com/matrix-org/matrix-react-sdk/pull/913) + * Fix 'missing page_type' error + [\#909](https://github.com/matrix-org/matrix-react-sdk/pull/909) + * code style update + [\#904](https://github.com/matrix-org/matrix-react-sdk/pull/904) + Changes in [0.8.9](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.8.9) (2017-05-22) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.9-rc.1...v0.8.9) From bceef2db916c519d637923b2dd8f504cbd0c0cea Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 02:15:49 +0100 Subject: [PATCH 117/416] v0.9.0-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6076a56d2..c17c8e83a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "0.8.9", + "version": "0.9.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 92d1a9a6ff0487b019cd3f63ddc30218522f3616 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 31 May 2017 22:00:30 -0400 Subject: [PATCH 118/416] enable useCompactLayout user setting an add a class when it's enabled Signed-off-by: Hubert Chathi --- src/components/structures/LoggedInView.js | 21 +++++++++++++++++++++ src/components/structures/UserSettings.js | 2 +- src/i18n/strings/en_EN.json | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 5f1aa0d32a..b9ef73dd42 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -18,6 +18,7 @@ limitations under the License. import * as Matrix from 'matrix-js-sdk'; import React from 'react'; +import UserSettingsStore from '../../UserSettingsStore'; import KeyCode from '../../KeyCode'; import Notifier from '../../Notifier'; import PageTypes from '../../PageTypes'; @@ -63,6 +64,13 @@ export default React.createClass({ }; }, + getInitialState: function() { + return { + // use compact timeline view + useCompactLayout: UserSettingsStore.getSyncedSetting('useCompactLayout'), + }; + }, + componentWillMount: function() { // stash the MatrixClient in case we log out before we are unmounted this._matrixClient = this.props.matrixClient; @@ -72,10 +80,12 @@ export default React.createClass({ this._scrollStateMap = {}; document.addEventListener('keydown', this._onKeyDown); + this._matrixClient.on("accountData", this.onAccountData); }, componentWillUnmount: function() { document.removeEventListener('keydown', this._onKeyDown); + this._matrixClient.removeListener("accountData", this.onAccountData); }, getScrollStateForRoom: function(roomId) { @@ -89,6 +99,14 @@ export default React.createClass({ return this.refs.roomView.canResetTimeline(); }, + onAccountData: function(event) { + if (event.getType() === "im.vector.web.settings") { + this.setState({ + useCompactLayout: event.getContent().useCompactLayout + }); + } + }, + _onKeyDown: function(ev) { /* // Remove this for now as ctrl+alt = alt-gr so this breaks keyboards which rely on alt-gr for numbers @@ -245,6 +263,9 @@ export default React.createClass({ if (topBar) { bodyClasses += ' mx_MatrixChat_toolbarShowing'; } + if (this.state.useCompactLayout) { + bodyClasses += ' mx_MatrixChat_useCompactLayout'; + } return (
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 725139de64..c545a75d45 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -79,11 +79,11 @@ const SETTINGS_LABELS = [ id: 'showTwelveHourTimestamps', label: 'Show timestamps in 12 hour format (e.g. 2:30pm)', }, -/* { id: 'useCompactLayout', label: 'Use compact timeline layout', }, +/* { id: 'useFixedWidthFont', label: 'Use fixed width font', diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4cf16b3d5d..a52082a236 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -517,6 +517,7 @@ "Upload Files": "Upload Files", "Upload file": "Upload file", "Usage": "Usage", + "Use compact timeline layout": "Use compact timeline layout", "Use with caution": "Use with caution", "User ID": "User ID", "User Interface": "User Interface", From 98c4252840b83ea287cf3f79e150d17e51daaaeb Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 1 Jun 2017 09:26:35 +0100 Subject: [PATCH 119/416] Throw errors when components don't look like components (#980) - instead of mysteriously throwing an error later on. --- src/Skinner.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Skinner.js b/src/Skinner.js index 4482f2239c..0688c9fc26 100644 --- a/src/Skinner.js +++ b/src/Skinner.js @@ -23,22 +23,28 @@ class Skinner { if (this.components === null) { throw new Error( "Attempted to get a component before a skin has been loaded."+ - "This is probably because either:"+ + " This is probably because either:"+ " a) Your app has not called sdk.loadSkin(), or"+ - " b) A component has called getComponent at the root level" + " b) A component has called getComponent at the root level", ); } - var comp = this.components[name]; - if (comp) { - return comp; - } + let comp = this.components[name]; // XXX: Temporarily also try 'views.' as we're currently // leaving the 'views.' off views. - var comp = this.components['views.'+name]; - if (comp) { - return comp; + if (!comp) { + comp = this.components['views.'+name]; } - throw new Error("No such component: "+name); + + if (!comp) { + throw new Error("No such component: "+name); + } + + // components have to be functions. + const validType = typeof comp === 'function'; + if (!validType) { + throw new Error(`Not a valid component: ${name}.`); + } + return comp; } load(skinObject) { From dd48b3f055c6197efb28d802b9b8eb29944229a5 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 1 Jun 2017 10:08:02 +0100 Subject: [PATCH 120/416] Add comment --- src/components/views/rooms/RoomList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index d672dea504..15f8b34312 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -529,6 +529,7 @@ module.exports = React.createClass({
; } + // We don't want to display drop targets if there are no room tiles to drag'n'drop if (this.state.totalRoomCount === 0) { return null; } From 63c9ec7d532f1871876a5f6496758540f4dadea9 Mon Sep 17 00:00:00 2001 From: Amandine Date: Thu, 1 Jun 2017 08:48:32 +0000 Subject: [PATCH 121/416] Translated using Weblate (French) Currently translated at 100.0% (770 of 770 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 47ab9c7573..5a0d16fcc3 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -128,7 +128,7 @@ "Displays action": "Affiche l'action", "Don't send typing notifications": "Ne pas envoyer les notifications de saisie", "Download %(text)s": "Télécharger %(text)s", - "Drop here %(toAction)s": "Déposer ici %(toAction)s", + "Drop here %(toAction)s": "Déposer ici pour %(toAction)s", "Drop here to tag %(section)s": "Déposer ici pour marque comme %(section)s", "Ed25519 fingerprint": "Empreinte Ed25519", "Email Address": "Adresse e-mail", @@ -723,7 +723,7 @@ "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contient des appareils que vous n'avez encore jamais vus.", "Unknown devices": "Appareils inconnus", "Unknown Address": "Adresse inconnue", - "Unblacklist": "Supprimer de la liste noire", + "Unblacklist": "Réhabiliter", "Blacklist": "Blacklister", "Unverify": "Non-vérifié", "Verify...": "Vérifier...", From 84fa154016df9777683fa7c8712cf9d806aec8a6 Mon Sep 17 00:00:00 2001 From: Krombel Date: Thu, 1 Jun 2017 11:02:48 +0000 Subject: [PATCH 122/416] Translated using Weblate (German) Currently translated at 100.0% (770 of 770 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 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 5850f8e3e4..7f79633a58 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -822,5 +822,7 @@ "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?": "Du wirst jetzt auf eine Drittanbieter-Website weitergeleitet, damit du dein Konto authentifizieren kannst für die Verwendung mit %(integrationsUrl)s. Möchtest du fortfahren?", "Disable URL previews for this room (affects only you)": "Deaktiviere die URL-Vorschau für diesen Raum (betrifft nur dich)", "Start automatically after system login": "Starte automatisch nach System-Login", - "Desktop specific": "Desktopspezifisch" + "Desktop specific": "Desktopspezifisch", + "Jump to first unread message.": "Springe zur ersten ungelesenen Nachricht.", + "Options": "Optionen" } From b26f4ba578d3c7422033273a9b6ee78a7e37eafa Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 1 Jun 2017 12:09:07 +0100 Subject: [PATCH 123/416] First lot of translations --- src/async-components/views/dialogs/ExportE2eKeysDialog.js | 4 ++-- src/async-components/views/dialogs/ImportE2eKeysDialog.js | 2 +- src/i18n/strings/en_EN.json | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/async-components/views/dialogs/ExportE2eKeysDialog.js b/src/async-components/views/dialogs/ExportE2eKeysDialog.js index 5abd758fa8..d6f16a7105 100644 --- a/src/async-components/views/dialogs/ExportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ExportE2eKeysDialog.js @@ -166,11 +166,11 @@ export default React.createClass({
-
diff --git a/src/async-components/views/dialogs/ImportE2eKeysDialog.js b/src/async-components/views/dialogs/ImportE2eKeysDialog.js index 75b66e2969..2622084222 100644 --- a/src/async-components/views/dialogs/ImportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ImportE2eKeysDialog.js @@ -164,7 +164,7 @@ export default React.createClass({
-
From 312109e846e92ccfee7175206dfaa065422d3db2 Mon Sep 17 00:00:00 2001 From: Jean GB Date: Thu, 1 Jun 2017 14:59:45 +0000 Subject: [PATCH 136/416] Translated using Weblate (French) Currently translated at 98.7% (771 of 781 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index fd22a54f57..00f4a74a37 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -128,7 +128,7 @@ "Displays action": "Affiche l'action", "Don't send typing notifications": "Ne pas envoyer les notifications de saisie", "Download %(text)s": "Télécharger %(text)s", - "Drop here %(toAction)s": "Déposer ici pour %(toAction)s", + "Drop here %(toAction)s": "Déposer ici %(toAction)s", "Drop here to tag %(section)s": "Déposer ici pour marque comme %(section)s", "Ed25519 fingerprint": "Empreinte Ed25519", "Email Address": "Adresse e-mail", From ab1e277f5fb3c935f244919123e51299dce22a98 Mon Sep 17 00:00:00 2001 From: turt2live Date: Thu, 1 Jun 2017 09:53:24 -0600 Subject: [PATCH 137/416] Support 12hr time on DateSeparator Signed-off-by: Travis Ralston --- src/components/structures/MessagePanel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6b80223e42..6f49e425fd 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -316,7 +316,7 @@ module.exports = React.createClass({ const key = "membereventlistsummary-" + (prevEvent ? mxEv.getId() : "initial"); if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) { - let dateSeparator =
  • ; + let dateSeparator =
  • ; ret.push(dateSeparator); } @@ -458,7 +458,7 @@ module.exports = React.createClass({ // do we need a date separator since the last event? if (this._wantsDateSeparator(prevEvent, eventDate)) { - var dateSeparator =
  • ; + var dateSeparator =
  • ; ret.push(dateSeparator); continuation = false; } From 66bce35918d10ca9a6babb09fa1571cf61e71908 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 1 Jun 2017 17:29:40 +0100 Subject: [PATCH 138/416] copyright adjustments --- src/autocomplete/AutocompleteProvider.js | 1 + src/autocomplete/Autocompleter.js | 2 +- src/autocomplete/CommandProvider.js | 1 + src/autocomplete/Components.js | 2 +- src/autocomplete/DuckDuckGoProvider.js | 1 + src/autocomplete/EmojiProvider.js | 1 + src/autocomplete/RoomProvider.js | 1 + src/autocomplete/UserProvider.js | 1 + 8 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/autocomplete/AutocompleteProvider.js b/src/autocomplete/AutocompleteProvider.js index 7988ab8a02..cbdb839ce3 100644 --- a/src/autocomplete/AutocompleteProvider.js +++ b/src/autocomplete/AutocompleteProvider.js @@ -1,4 +1,5 @@ /* +Copyright 2016 Aviral Dasgupta Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/autocomplete/Autocompleter.js b/src/autocomplete/Autocompleter.js index 8678321ab2..f8564a43a0 100644 --- a/src/autocomplete/Autocompleter.js +++ b/src/autocomplete/Autocompleter.js @@ -1,5 +1,5 @@ /* -Copyright 2017 Vector Creations Ltd +Copyright 2016 Aviral Dasgupta 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/autocomplete/CommandProvider.js b/src/autocomplete/CommandProvider.js index fe9a7bae12..205a3737dc 100644 --- a/src/autocomplete/CommandProvider.js +++ b/src/autocomplete/CommandProvider.js @@ -1,4 +1,5 @@ /* +Copyright 2016 Aviral Dasgupta Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/autocomplete/Components.js b/src/autocomplete/Components.js index e553fef079..b26a217ec6 100644 --- a/src/autocomplete/Components.js +++ b/src/autocomplete/Components.js @@ -1,5 +1,5 @@ /* -Copyright 2017 Vector Creations Ltd +Copyright 2016 Aviral Dasgupta 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/autocomplete/DuckDuckGoProvider.js b/src/autocomplete/DuckDuckGoProvider.js index 5dae1b46d4..9c996bb1cc 100644 --- a/src/autocomplete/DuckDuckGoProvider.js +++ b/src/autocomplete/DuckDuckGoProvider.js @@ -1,4 +1,5 @@ /* +Copyright 2016 Aviral Dasgupta Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index ca434072cb..810212315b 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -1,4 +1,5 @@ /* +Copyright 2016 Aviral Dasgupta Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index b6c8c2f263..be35c53e5d 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -1,4 +1,5 @@ /* +Copyright 2016 Aviral Dasgupta Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 4dc867ba55..fedebb3618 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -1,4 +1,5 @@ /* +Copyright 2016 Aviral Dasgupta Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); From 6e693612a0550822b4396cf01eea6dc31a898967 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 1 Jun 2017 17:49:49 +0100 Subject: [PATCH 139/416] travisci: Don't run the riot-web tests if the react-sdk tests fail --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a8f804644..a405b9ef35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,6 @@ install: - npm install - (cd node_modules/matrix-js-sdk && npm install) script: - - npm run test - - ./.travis-test-riot.sh + # don't run the riot tests unless the react-sdk tests pass, otherwise + # the output is confusing. + - npm run test && ./.travis-test-riot.sh From b3e97161268c3e5a2bf6a41748e289564fb29db7 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 1 Jun 2017 17:52:25 +0100 Subject: [PATCH 140/416] Revert "add labels to language picker" --- .../views/elements/LanguageDropdown.js | 9 ++++++++- src/languageHandler.js | 19 +++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js index 49f89aa469..25a920d2e0 100644 --- a/src/components/views/elements/LanguageDropdown.js +++ b/src/components/views/elements/LanguageDropdown.js @@ -40,7 +40,14 @@ export default class LanguageDropdown extends React.Component { } componentWillMount() { - languageHandler.getAllLanguagesFromJson().then((langs) => { + languageHandler.getAllLanguageKeysFromJson().then((langKeys) => { + const langs = []; + langKeys.forEach((languageKey) => { + langs.push({ + value: languageKey, + label: _t(languageKey) + }); + }); langs.sort(function(a, b){ if(a.label < b.label) return -1; if(a.label > b.label) return 1; diff --git a/src/languageHandler.js b/src/languageHandler.js index ab29dd926e..1c3acab082 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -133,7 +133,7 @@ export function setLanguage(preferredLangs) { throw new Error("Unable to find an appropriate language"); } - return getLanguage(i18nFolder + availLangs[langToUse].fileName); + return getLanguage(i18nFolder + availLangs[langToUse]); }).then((langData) => { counterpart.registerTranslations(langToUse, langData); counterpart.setLocale(langToUse); @@ -142,25 +142,16 @@ export function setLanguage(preferredLangs) { // Set 'en' as fallback language: if (langToUse != "en") { - return getLanguage(i18nFolder + availLangs['en'].fileName); + return getLanguage(i18nFolder + availLangs['en']); } }).then((langData) => { if (langData) counterpart.registerTranslations('en', langData); }); }; -export function getAllLanguagesFromJson() { - return getLangsJson().then((langsObject) => { - var langs = []; - for (var langKey in langsObject) { - if (langsObject.hasOwnProperty(langKey)) { - langs.push({ - 'value': langKey, - 'label': langsObject[langKey].label - }); - } - } - return langs; +export function getAllLanguageKeysFromJson() { + return getLangsJson().then((langs) => { + return Object.keys(langs); }); } From 16c4c14a16acf40650f61f338e241c4e6c67fbdf Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 1 Jun 2017 18:01:30 +0100 Subject: [PATCH 141/416] Fix to show the correct room --- src/components/structures/RoomView.js | 9 +++- src/stores/RoomViewStore.js | 65 ++++++++++++++++++--------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 2189fa3856..9a930d3d06 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -171,7 +171,7 @@ module.exports = React.createClass({ }); // Start listening for RoomViewStore updates - RoomViewStore.addListener(this._onRoomViewStoreUpdate); + this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); this._onRoomViewStoreUpdate(true); }, @@ -182,6 +182,8 @@ module.exports = React.createClass({ this.setState({ roomId: RoomViewStore.getRoomId(), roomAlias: RoomViewStore.getRoomAlias(), + roomLoading: RoomViewStore.isRoomLoading(), + roomLoadError: RoomViewStore.getRoomLoadError(), joining: RoomViewStore.isJoining(), joinError: RoomViewStore.getJoinError(), }, () => { @@ -343,6 +345,11 @@ module.exports = React.createClass({ document.removeEventListener("keydown", this.onKeyDown); + // Remove RoomStore listener + if (this._roomStoreToken) { + this._roomStoreToken.remove(); + } + // cancel any pending calls to the rate_limited_funcs this._updateRoomMembers.cancelPendingCall(); diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index d893318af7..a74652a39d 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -58,6 +58,9 @@ class RoomViewStore extends Store { case 'view_room': this._viewRoom(payload); break; + case 'view_room_error': + this._viewRoomError(payload); + break; case 'will_join': this._setState({ joining: true, @@ -80,31 +83,45 @@ class RoomViewStore extends Store { } _viewRoom(payload) { - const address = payload.room_alias || payload.room_id; - if (address[0] == '#') { + // Always set the room ID if present + if (payload.room_id) { this._setState({ - roomLoading: true, - }); - MatrixClientPeg.get().getRoomIdForAlias(address).then( - (result) => { - this._setState({ - roomId: result.room_id, - roomAlias: address, - roomLoading: false, - roomLoadError: null, - }); - }, (err) => { - console.error(err); - this._setState({ - roomLoading: false, - roomLoadError: err, - }); - }); - } else { - this._setState({ - roomId: address, + roomId: payload.room_id, }); } + + if (payload.room_alias && !payload.room_id) { + this._setState({ + roomId: null, + roomAlias: payload.room_alias, + roomLoading: true, + roomLoadError: null, + }); + MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias).done( + (result) => { + dis.dispatch({ + action: 'view_room', + room_id: result.room_id, + room_alias: payload.room_alias, + }); + }, (err) => { + dis.dispatch({ + action: 'view_room_error', + room_id: null, + room_alias: payload.room_alias, + err: err, + }); + }); + } + } + + _viewRoomError(payload) { + this._setState({ + roomId: payload.room_id, + roomAlias: payload.room_alias, + roomLoading: false, + roomLoadError: payload.err, + }); } _joinRoom(payload) { @@ -140,6 +157,10 @@ class RoomViewStore extends Store { return this._state.roomLoading; } + getRoomLoadError() { + return this._state.roomLoadError; + } + isJoining() { return this._state.joining; } From a2b931ee911f2b7dcb81166e3afe4710790e7dbd Mon Sep 17 00:00:00 2001 From: Amandine Date: Thu, 1 Jun 2017 17:09:46 +0000 Subject: [PATCH 142/416] Translated using Weblate (French) Currently translated at 100.0% (781 of 781 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 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 00f4a74a37..695c3b7222 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -771,5 +771,15 @@ "Jump to first unread message.": "Aller au premier message non-lu.", "Options": "Options", "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?": "Vous êtes sur le point d’accéder à un site tiers afin de pouvoir vous identifier pour utiliser %(integrationsUrl)s. Voulez vous continuer ?", - "Removed or unknown message type": "Type de message inconnu ou supprimé" + "Removed or unknown message type": "Type de message inconnu ou supprimé", + "disabled": "désactivé", + "enabled": "activé", + "Set a Display Name": "Définir un nom d’affichage", + "for %(amount)ss": "depuis %(amount)ss", + "for %(amount)sm": "depuis %(amount)sm", + "for %(amount)sh": "depuis %(amount)sh", + "for %(amount)sd": "depuis %(amount)sj", + "$senderDisplayName changed the room avatar to ": "$senderDisplayName a changé l’image de profil du salon en ", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s a supprimé l’image de profil du salon.", + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s a changé l’image de profil de %(roomName)s" } From 3ffa3d82c64972ce90de0e069847a479470aacef Mon Sep 17 00:00:00 2001 From: Bamstam Date: Thu, 1 Jun 2017 17:08:10 +0000 Subject: [PATCH 143/416] Translated using Weblate (German) Currently translated at 99.6% (778 of 781 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 | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 7c4b6bcab3..00de1617a1 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -18,7 +18,7 @@ "Name": "Name", "Device ID": "Geräte-ID", "Verification": "Verifizierung", - "Ed25519 fingerprint": "Ed25519 Fingerprint", + "Ed25519 fingerprint": "Ed25519-Fingerprint", "User ID": "Benutzer-ID", "Curve25519 identity key": "Curve25519-Identitäts-Schlüssel", "Claimed Ed25519 fingerprint key": "Geforderter Ed25519 Fingerprint Schlüssel", @@ -110,7 +110,7 @@ "*️⃣ Commands": "*️⃣ Befehle", "Default": "Standard", "demote": "Zum zurückstufen", - "Export E2E room keys": "Exportiere E2E-Raum-Schlüssel", + "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 forget room": "Vergessen des Raums schlug fehl", "Failed to leave room": "Verlassen des Raums fehlgeschlagen", @@ -150,7 +150,7 @@ "Logged in as": "Angemeldet als", "Logout": "Abmelden", "made future room history visible to": "mache kommende Raum-Historie sichtbar für", - "Manage Integrations": "Verwalte Integrationen", + "Manage Integrations": "Integrationen verwalten", "Members only": "Nur Mitglieder", "Mobile phone number": "Mobile Telefonnummer", "Moderator": "Moderator", @@ -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?": "Lösche Kontakt-Informationen?", + "Remove Contact Information?": "Kontakt-Informationen löschen?", "removed their display name": "löschte den eigenen Anzeigenamen", "Remove": "Entfernen", "requested a VoIP conference": "hat eine VoIP-Konferenz angefordert", @@ -192,8 +192,8 @@ "Room Colour": "Raumfarbe", "Room name (optional)": "Raumname (optional)", "Scroll to unread messages": "Scrolle zu ungelesenen Nachrichten", - "Send Invites": "Sende Einladungen", - "Send Reset Email": "Sende Rücksetz-E-mail", + "Send Invites": "Einladungen senden", + "Send Reset Email": "E-Mail für das Zurücksetzen senden", "sent an image": "sandte ein Bild", "sent an invitation to": "sandte eine Einladung an", "sent a video": "sandte ein Video", @@ -216,7 +216,7 @@ "their invitation": "ihre Einladung", "These are experimental features that may break in unexpected ways. Use with caution": "Dies sind experimentelle Funktionen die in unerwarteter Weise Fehler verursachen können. Mit Vorsicht benutzen", "The visibility of existing history will be unchanged": "Die Sichtbarkeit der existenten Historie bleibt unverändert", - "This doesn't appear to be a valid email address": "Die scheint keine valide E-Mail-Adresse zu sein", + "This doesn't appear to be a valid email address": "Dies scheint keine gültige E-Mail-Adresse zu sein", "this invitation?": "diese Einladung?", "This is a preview of this room. Room interactions have been disabled": "Dies ist eine Vorschau dieses Raumes. Raum-Interaktionen wurden deaktiviert", "This room is not accessible by remote Matrix servers": "Dieser Raum ist über entfernte Matrix-Server nicht zugreifbar", @@ -239,7 +239,7 @@ "To reset your password, enter the email address linked to your account": "Um dein Passwort zurückzusetzen, gib bitte die mit deinem Account verknüpfte E-Mail-Adresse ein", "To send messages": "Zum Nachrichten senden", "turned on end-to-end encryption (algorithm": "aktivierte Ende-zu-Ende-Verschlüsselung (Algorithmus", - "Unable to add email address": "Unfähig die E-Mail-Adresse hinzuzufügen", + "Unable to add email address": "E-Mail-Adresse konnte nicht hinzugefügt werden", "Unable to remove contact information": "Unfähig die Kontakt-Informationen zu löschen", "Unable to verify email address.": "Unfähig die E-Mail-Adresse zu verifizieren.", "Unban": "Entbannen", @@ -257,7 +257,7 @@ "Verification Pending": "Verifizierung ausstehend", "Video call": "Videoanruf", "Voice call": "Sprachanruf", - "VoIP conference finished.": "VoIP-Konferenz beendet.", + "VoIP conference finished.": "VoIP-Konferenz wurde beendet.", "VoIP conference started.": "VoIP-Konferenz gestartet.", "(warning: cannot be disabled again!)": "(Warnung: Kann nicht wieder deaktiviert werden!)", "was banned": "wurde aus dem Raum verbannt", @@ -266,7 +266,7 @@ "was unbanned": "wurde entbannt", "was": "wurde", "Who can access this room?": "Wer hat Zugang zu diesem Raum?", - "Who can read history?": "Wer kann die Historie lesen?", + "Who can read history?": "Wer kann die Chat-Historie lesen?", "Who would you like to add to this room?": "Wen möchtest du zu diesem Raum hinzufügen?", "Who would you like to communicate with?": "Mit wem möchtest du kommunizieren?", "Would you like to": "Möchtest du", @@ -285,8 +285,8 @@ "Conference calls are not supported in encrypted rooms": "Konferenzgespräche sind in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche sind in diesem Client nicht unterstützt", "Existing Call": "Existierender Anruf", - "Failed to set up conference call": "Aufbau des Konferenzgesprächs fehlgeschlagen", - "Failed to verify email address: make sure you clicked the link in the email": "Verifizierung der E-Mail-Adresse fehlgeschlagen: Bitte stelle sicher, dass du den Link in der E-Mail anklickt hast", + "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", "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", @@ -301,7 +301,7 @@ "Unable to capture screen": "Unfähig den Bildschirm aufzunehmen", "Unable to enable Notifications": "Unfähig Benachrichtigungen zu aktivieren", "Upload Failed": "Upload fehlgeschlagen", - "VoIP is unsupported": "VoIP ist nicht unterstützt", + "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", @@ -328,7 +328,7 @@ "Dec": "Dez", "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(time)s", "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", - "Set a display name:": "Setze einen Anzeigenamen:", + "Set a display name:": "Anzeigename eingeben:", "Upload an avatar:": "Lade einen Avatar hoch:", "This server does not support authentication with a phone number.": "Dieser Server unterstützt keine Authentifizierung mittels Telefonnummer.", "Missing password.": "Fehlendes Passwort.", @@ -352,7 +352,7 @@ "%(names)s and %(count)s others are typing": "%(names)s und %(count)s weitere Personen tippen", "%(senderName)s answered the call.": "%(senderName)s beantwortete den Anruf.", "%(senderName)s banned %(targetName)s.": "%(senderName)s hat %(targetName)s aus dem Raum verbannt.", - "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s änderte den Anzeigenamen von %(oldDisplayName)s zu %(displayName)s.", + "%(senderName)s changed their 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 änderte das Profilbild.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s.", @@ -579,7 +579,7 @@ "Failed to mute user": "Nutzer lautlos zu stellen fehlgeschlagen", "Failed to reject invite": "Einladung abzulehnen fehlgeschlagen", "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", - "Failed to set display name": "Anzeigenamen zu ändern fehlgeschlagen", + "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", @@ -709,7 +709,7 @@ "Interface Language": "Oberflächen-Sprache", "Logged in as:": "Angemeldet als:", "matrix-react-sdk version:": "Version von matrix-react-sdk:", - "New passwords don't match": "Neue Passwörter nicht gleich", + "New passwords don't match": "Die neuen Passwörter stimmen nicht überein", "olm version:": "Version von olm:", "Passwords can't be empty": "Passwortfelder dürfen nicht leer sein", "Registration required": "Registrierung benötigt", @@ -735,7 +735,7 @@ "Enter passphrase": "Passphrase eingeben", "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 seine Dateien zu sehen", + "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": "Lehne alle %(invitedRooms)s Einladungen ab", "Start new Chat": "Starte neuen Chat", @@ -749,7 +749,7 @@ "To continue, please enter your password.": "Zum fortfahren bitte Passwort eingeben.", "Device name": "Geräte-Name", "Device key": "Geräte-Schlüssel", - "In future this verification process will be more sophisticated.": "In Zukunft wird der Verifikationsprozess eleganter.", + "In future this verification process will be more sophisticated.": "Zukünftig wird dieser Verifizierungsprozess technisch ausgereifter und eleganter gestaltet werden.", "Verify device": "Gerät verifizieren", "I verify that the keys match": "Ich bestätige, dass die Schlüssel passen", "Unable to restore session": "Sitzungswiederherstellung fehlgeschlagen", @@ -764,7 +764,7 @@ "Add User": "Nutzer hinzufügen", "Sign in with CAS": "Mit CAS anmelden", "Custom Server Options": "Erweiterte Server-Optionen", - "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Du kannst die erweiterten Server-Optionen nutzen um dich an anderen Matrix-Servern anzumelden indem die eine andere Heimserver-URL angibst.", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Du kannst die erweiterten Server-Optionen nutzen, um dich an anderen Matrix-Servern anzumelden, indem du eine andere Heimserver-URL angibst.", "This allows you to use this app with an existing Matrix account on a different home server.": "Dies erlaubt dir diese App mit einem existierenden Matrix-Konto auf einem anderen Heimserver zu verwenden.", "Dismiss": "Ablehnen", "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "Du kannst auch einen angepassten Idantitätsserver angeben aber dies wird typischerweise Interaktionen mit anderen Nutzern auf Basis der E-Mail-Adresse verhindern.", @@ -779,7 +779,7 @@ "Custom server": "Angepasster Server", "Home server URL": "Heimserver-URL", "Identity server URL": "Identitätsserver-URL", - "What does this mean?": "Was bedeutet es?", + "What does this mean?": "Was bedeutet das?", "Error decrypting audio": "Audio-Entschlüsselung fehlgeschlagen", "Error decrypting image": "Bild-Entschlüsselung fehlgeschlagen", "Image '%(Body)s' cannot be displayed.": "Das Bild '%(Body)s' kann nicht angezeigt werden.", @@ -808,7 +808,7 @@ "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:": "Um zu bestätigen, dass diesem Gerät vertraut werden kann, kontaktiere bitte den Eigentümer über einen anderen Weg (z.B. Telefon-Anruf) und frage, ob der Schlüssel, den sie in den Nutzer-Einstellungen für dieses Gerät sehen dem folgenden gleicht:", "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.": "Wenn er passt, betätige den Bestätigen-Button unten. Wenn nicht, fängt jemand anderes dieses Gerät ab und du möchtest wahrscheinlich lieber den Blacklist-Button betätigen.", "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.": "Bei der Wiederherstellung deiner vorherigen Sitzung ist ein Fehler aufgetreten. Um fortzufahren, musst du dich erneut anmelden. Eine zuvor verschlüsselte Chat-Historie wird in der Folge nicht mehr lesbar sein.", - "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.": "Wenn du vorher eine aktuellere Version von Riot verwendet hast, ist deine Sitzung wohlmöglich inkompatibel mit dieser Version. Schließe dieses Fenster und kehre zur aktuelleren Version zurück.", + "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.": "Wenn du zuvor eine aktuellere Version von Riot verwendet hast, ist deine Sitzung eventuell inkompatibel mit dieser Version. Bitte schließe dieses Fenster und kehre zur aktuelleren Version zurück.", "Blacklist": "Blockieren", "Unblacklist": "Entblockieren", "Unverify": "Entverifizieren", @@ -817,7 +817,7 @@ "Idle": "inaktiv", "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.": "Wir empfehlen dir für jedes Gerät durch den Verifizierungsprozess zu gehen um zu bestätigen, dass sie ihrem legitimierten Besitzer gehören, aber du kannst die Nachrichten ohne Verifizierung erneut senden, wenn du es vorziehst.", "Ongoing conference call%(supportedText)s. %(joinText)s": "Laufendes Konferenzgespräch%(supportedText)s. %(joinText)s", - "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?": "Du wirst jetzt auf eine Drittanbieter-Website weitergeleitet, damit du dein Konto authentifizieren kannst für die Verwendung mit %(integrationsUrl)s. Möchtest du fortfahren?", + "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?": "Du wirst jetzt auf die Website eines Drittanbieters weitergeleitet, damit du dein Konto für die Verwendung von %(integrationsUrl)s authentifizieren kannst. Möchtest du fortfahren?", "Disable URL previews for this room (affects only you)": "Deaktiviere die URL-Vorschau für diesen Raum (betrifft nur dich)", "Start automatically after system login": "Starte automatisch nach System-Login", "Desktop specific": "Desktopspezifisch", From 9f8b2ba4ba1d31ee92a6ae1340daabfb63722d57 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 1 Jun 2017 18:58:07 +0100 Subject: [PATCH 144/416] maybe fixxy? Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- karma.conf.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/karma.conf.js b/karma.conf.js index 4ad72b4927..d544248332 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -177,6 +177,11 @@ module.exports = function (config) { ], }, devtool: 'inline-source-map', + externals: { + // Don't try to bundle electron: leave it as a commonjs dependency + // (the 'commonjs' here means it will output a 'require') + "electron": "commonjs electron", + }, }, webpackMiddleware: { From 46750c4b9b5efe8a812df3b989c84642088920e4 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 1 Jun 2017 20:40:27 +0200 Subject: [PATCH 145/416] Fix tests for PR #989 --- test/i18n/languages.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/i18n/languages.json b/test/i18n/languages.json index bdb46584b9..e3a2595370 100644 --- a/test/i18n/languages.json +++ b/test/i18n/languages.json @@ -1,3 +1,4 @@ { - "en": "en_EN.json" + "fileName": "en_EN.json", + "label": "English" } From 8e34b59d32929828c91b44a1cd5e11601d02f635 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 19:46:25 +0100 Subject: [PATCH 146/416] Revert "Revert "add labels to language picker"" --- .../views/elements/LanguageDropdown.js | 9 +-------- src/languageHandler.js | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js index 25a920d2e0..49f89aa469 100644 --- a/src/components/views/elements/LanguageDropdown.js +++ b/src/components/views/elements/LanguageDropdown.js @@ -40,14 +40,7 @@ export default class LanguageDropdown extends React.Component { } componentWillMount() { - languageHandler.getAllLanguageKeysFromJson().then((langKeys) => { - const langs = []; - langKeys.forEach((languageKey) => { - langs.push({ - value: languageKey, - label: _t(languageKey) - }); - }); + languageHandler.getAllLanguagesFromJson().then((langs) => { langs.sort(function(a, b){ if(a.label < b.label) return -1; if(a.label > b.label) return 1; diff --git a/src/languageHandler.js b/src/languageHandler.js index 1c3acab082..ab29dd926e 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -133,7 +133,7 @@ export function setLanguage(preferredLangs) { throw new Error("Unable to find an appropriate language"); } - return getLanguage(i18nFolder + availLangs[langToUse]); + return getLanguage(i18nFolder + availLangs[langToUse].fileName); }).then((langData) => { counterpart.registerTranslations(langToUse, langData); counterpart.setLocale(langToUse); @@ -142,16 +142,25 @@ export function setLanguage(preferredLangs) { // Set 'en' as fallback language: if (langToUse != "en") { - return getLanguage(i18nFolder + availLangs['en']); + return getLanguage(i18nFolder + availLangs['en'].fileName); } }).then((langData) => { if (langData) counterpart.registerTranslations('en', langData); }); }; -export function getAllLanguageKeysFromJson() { - return getLangsJson().then((langs) => { - return Object.keys(langs); +export function getAllLanguagesFromJson() { + return getLangsJson().then((langsObject) => { + var langs = []; + for (var langKey in langsObject) { + if (langsObject.hasOwnProperty(langKey)) { + langs.push({ + 'value': langKey, + 'label': langsObject[langKey].label + }); + } + } + return langs; }); } From cbf967a86d9cc5de38a4e600b8c67f76450d39a4 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 1 Jun 2017 20:53:59 +0200 Subject: [PATCH 147/416] Fix tests We do not have a en.json but a en_EN.json --- src/languageHandler.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index ab29dd926e..65e82a7c81 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -141,11 +141,11 @@ export function setLanguage(preferredLangs) { console.log("set language to " + langToUse); // Set 'en' as fallback language: - if (langToUse != "en") { - return getLanguage(i18nFolder + availLangs['en'].fileName); + if (langToUse != "en_EN") { + return getLanguage(i18nFolder + availLangs['en_EN'].fileName); } }).then((langData) => { - if (langData) counterpart.registerTranslations('en', langData); + if (langData) counterpart.registerTranslations('en_EN', langData); }); }; From 51131ef7a54e52e6bafd3a37fcea4d8357fdc66a Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 1 Jun 2017 21:02:40 +0200 Subject: [PATCH 148/416] Fix translation tests part 2 --- src/languageHandler.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index 65e82a7c81..feb91cf2ef 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -130,7 +130,9 @@ export function setLanguage(preferredLangs) { } } if (!langToUse) { - throw new Error("Unable to find an appropriate language"); + // Fallback to en_EN if none is found + langToUse = 'en_EN' + console.error("Unable to find an appropriate language"); } return getLanguage(i18nFolder + availLangs[langToUse].fileName); @@ -142,7 +144,7 @@ export function setLanguage(preferredLangs) { // Set 'en' as fallback language: if (langToUse != "en_EN") { - return getLanguage(i18nFolder + availLangs['en_EN'].fileName); + return getLanguage(i18nFolder + availLangs[langToUse].fileName); } }).then((langData) => { if (langData) counterpart.registerTranslations('en_EN', langData); From 924a8d1be041f83eeef9650e3b0798b710812bb4 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 1 Jun 2017 21:03:43 +0200 Subject: [PATCH 149/416] Fix line change that should not happen --- src/languageHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index feb91cf2ef..33f9781f99 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -144,7 +144,7 @@ export function setLanguage(preferredLangs) { // Set 'en' as fallback language: if (langToUse != "en_EN") { - return getLanguage(i18nFolder + availLangs[langToUse].fileName); + return getLanguage(i18nFolder + availLangs['en_EN'].fileName); } }).then((langData) => { if (langData) counterpart.registerTranslations('en_EN', langData); From 1b35f816fba21e880577c26eb03f3a0792e96c09 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 1 Jun 2017 21:10:32 +0200 Subject: [PATCH 150/416] Fix languages.json --- test/i18n/languages.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/i18n/languages.json b/test/i18n/languages.json index e3a2595370..5dc02003f7 100644 --- a/test/i18n/languages.json +++ b/test/i18n/languages.json @@ -1,4 +1,6 @@ { - "fileName": "en_EN.json", - "label": "English" + "en": { + "fileName": "en_EN.json", + "label": "English" + } } From 8c2728ffc52a6ae60b3c19934e94cdc934cb0995 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 1 Jun 2017 21:10:58 +0200 Subject: [PATCH 151/416] Revert changes of the key used --- src/languageHandler.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/languageHandler.js b/src/languageHandler.js index 33f9781f99..798798b6e5 100644 --- a/src/languageHandler.js +++ b/src/languageHandler.js @@ -131,7 +131,7 @@ export function setLanguage(preferredLangs) { } if (!langToUse) { // Fallback to en_EN if none is found - langToUse = 'en_EN' + langToUse = 'en' console.error("Unable to find an appropriate language"); } @@ -143,11 +143,11 @@ export function setLanguage(preferredLangs) { console.log("set language to " + langToUse); // Set 'en' as fallback language: - if (langToUse != "en_EN") { - return getLanguage(i18nFolder + availLangs['en_EN'].fileName); + if (langToUse != "en") { + return getLanguage(i18nFolder + availLangs['en'].fileName); } }).then((langData) => { - if (langData) counterpart.registerTranslations('en_EN', langData); + if (langData) counterpart.registerTranslations('en', langData); }); }; From 650d45466c78c69d00fd1c61d55debfe70a0d21d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 1 Jun 2017 22:06:02 +0100 Subject: [PATCH 152/416] fix up missing strings caused by punctuation changes --- scripts/fix-i18n.pl | 9 +++++++++ src/i18n/strings/de_DE.json | 18 +++++++++--------- src/i18n/strings/es.json | 6 +++--- src/i18n/strings/fr.json | 18 +++++++++--------- src/i18n/strings/nl.json | 4 ++-- src/i18n/strings/pt.json | 16 ++++++++-------- src/i18n/strings/pt_BR.json | 18 +++++++++--------- src/i18n/strings/ru.json | 14 +++++++------- src/i18n/strings/zh_Hans.json | 36 +++++++++++++++++------------------ src/i18n/strings/zh_Hant.json | 6 +++--- 10 files changed, 77 insertions(+), 68 deletions(-) diff --git a/scripts/fix-i18n.pl b/scripts/fix-i18n.pl index 9a12c9d909..247b2b663f 100755 --- a/scripts/fix-i18n.pl +++ b/scripts/fix-i18n.pl @@ -52,6 +52,15 @@ Guest users can't invite users. Please register to invite. This room is inaccessible to guests. You may be able to join if you register. delete the alias. remove %(name)s from the directory. +Conference call failed. +Conference calling is in development and may not be reliable. +Guest users can't create new rooms. Please register to create room and start a chat. +Server may be unavailable, overloaded, or you hit a bug. +Server unavailable, overloaded, or something else went wrong. +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. EOT )]; } diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 00de1617a1..baf2b317cf 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 sind in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche sind in diesem Client nicht unterstützt", "Existing Call": "Existierender Anruf", "Failed to set up conference call": "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": "Unfähig Benachrichtigungen zu aktivieren", "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", @@ -608,7 +608,7 @@ "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", + "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", diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 849a685667..e071e63a3a 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -191,8 +191,8 @@ "Click to unmute audio": "Haz clic para activar sonido de audio", "Command error": "Error de comando", "Commands": "Comandos", - "Conference call failed": "La llamada de conferencia falló", - "Conference calling is in development and may not be reliable": "La llamada en conferencia esta en desarrollo y no podría ser segura", + "Conference call failed.": "La llamada de conferencia falló.", + "Conference calling is in development and may not be reliable.": "La llamada en conferencia esta en desarrollo y no podría ser segura.", "Conference calls are not supported in encrypted rooms": "Las llamadas en conferencia no son soportadas en salas encriptadas", "Conference calls are not supported in this client": "Las llamadas en conferencia no son soportadas en este navegador", "Confirm password": "Confirmar clave", @@ -281,7 +281,7 @@ "Found a bug?": "¿Encontraste un error?", "%(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 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", "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.", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 695c3b7222..fbcc04e881 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -232,8 +232,8 @@ "Click to unmute audio": "Cliquer pour rétablir le son", "Command error": "Erreur de commande", "Commands": "Commandes", - "Conference call failed": "Échec de la conférence", - "Conference calling is in development and may not be reliable": "Les appels en conférence sont encore en développement et sont potentiellement peu fiables", + "Conference call failed.": "Échec de la conférence.", + "Conference calling is in development and may not be reliable.": "Les appels en conférence sont encore en développement et sont potentiellement peu fiables.", "Conference calls are not supported in encrypted rooms": "Les appels en conférence ne sont pas supportés dans les salons encryptés", "Conference calls are not supported in this client": "Les appels en conférence ne sont pas supportés avec ce client", "Confirm password": "Confirmer le mot de passe", @@ -293,7 +293,7 @@ "For security, this session has been signed out. Please sign in again.": "Par sécurité, la session a expiré. Merci de vous authentifer à nouveau.", "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 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", "had": "avait", "Hangup": "Raccrocher", @@ -444,8 +444,8 @@ "Server may be unavailable or overloaded": "Le serveur semble être inaccessible ou surchargé", "Server may be unavailable, overloaded, or search timed out :(": "Le serveur semble être inaccessible, surchargé ou la recherche a expiré :(", "Server may be unavailable, overloaded, or the file too big": "Le serveur semble être inaccessible, surchargé ou le fichier trop important", - "Server may be unavailable, overloaded, or you hit a bug": "Le serveur semble être inaccessible, surchargé ou vous avez rencontré un problème", - "Server unavailable, overloaded, or something else went wrong": "Le serveur semble être inaccessible, surchargé ou quelque chose s'est mal passé", + "Server may be unavailable, overloaded, or you hit a bug.": "Le serveur semble être inaccessible, surchargé ou vous avez rencontré un problème.", + "Server unavailable, overloaded, or something else went wrong.": "Le serveur semble être inaccessible, surchargé ou quelque chose s'est mal passé.", "Session ID": "Identifiant de session", "%(senderName)s set a profile picture.": "%(senderName)s a défini une photo de profil.", "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s a défini son nom d’affichage comme %(displayName)s.", @@ -556,11 +556,11 @@ "Who would you like to communicate with?": "Avec qui voulez-vous communiquer ?", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s a révoqué l’invitation de %(targetName)s.", "Would you like to": "Voulez-vous", - "You are already in a call": "Vous êtes déjà dans un appel", + "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 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 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", "You have been invited to join this room by %(inviterName)s": "Vous avez été invité à joindre ce salon par %(inviterName)s", "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Vous avez été déconnecté de tous vos appareils et ne recevrez plus de notifications. Pour réactiver les notificationsm identifiez vous à nouveau sur tous les appareils", @@ -570,7 +570,7 @@ "You need to be logged in.": "Vous devez être connecté.", "You need to enter a user name.": "Vous devez entrer un nom d’utilisateur.", "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.": "Vous devez vous connecter à nouveau pour générer les clés d’encryption pour cet appareil, et soumettre la clé publique à votre homeserver. Cette action ne se reproduira pas; veuillez nous excuser pour la gêne occasionnée.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Votre adresse e-mail ne semble pas associée à un identifiant Matrix sur ce homeserver", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Votre adresse e-mail ne semble pas associée à un identifiant Matrix sur ce homeserver.", "Your password has been reset": "Votre mot de passe a été réinitialisé", "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Votre mot de passe a été mis à jour avec succès. Vous ne recevrez plus de notification sur vos appareils jusqu’à ce que vous vous identifiez à nouveau", "You seem to be in a call, are you sure you want to quit?": "Vous semblez avoir un appel en cours, êtes-vous sûr(e) de vouloir quitter ?", diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 668fd2ba32..b89c360792 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -191,8 +191,8 @@ "Click to unmute audio": "Klik om het dempen van het geluid op te heffen", "Command error": "Opdracht fout", "Commands": "Opdrachten", - "Conference call failed": "Conferentie gesprek mislukt", - "Conference calling is in development and may not be reliable": "Conferentie gesprekken zijn nog in ontwikkelingen en kunnen onbetrouwbaar zijn", + "Conference call failed.": "Conferentie gesprek mislukt.", + "Conference calling is in development and may not be reliable.": "Conferentie gesprekken zijn nog in ontwikkelingen en kunnen onbetrouwbaar zijn.", "Conference calls are not supported in encrypted rooms": "Conferentie gesprekken worden niet ondersteunt in versleutelde kamers", "Conference calls are not supported in this client": "Conferentie gesprekken worden niet ondersteunt in deze client", "Confirm password": "Bevestigen wachtwoord", diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index f57ee4109a..ad4a9774d6 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -188,7 +188,7 @@ "sent an invitation to": "enviou um convite para", "sent a video": "enviou um vídeo", "Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado", - "Server may be unavailable, overloaded, or you hit a bug": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução", + "Server may be unavailable, overloaded, or you hit a bug.": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução.", "Session ID": "Identificador de sessão", "set a profile picture": "colocou uma foto de perfil", "set their display name to": "configurou seu nome para", @@ -320,8 +320,8 @@ "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s.", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s alterou o tópico para \"%(topic)s\".", "click to reveal": "clique para ver", - "Conference call failed": "Chamada de conferência falhou", - "Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar", + "Conference call failed.": "Chamada de conferência falhou.", + "Conference calling is in development and may not be reliable.": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar.", "Conference calls are not supported in encrypted rooms": "Chamadas de conferência não são possíveis em salas criptografadas", "Conference calls are not supported in this client": "Chamadas de conferência não são possíveis neste navegador", "/ddg is not a command": "/ddg não é um comando", @@ -336,7 +336,7 @@ "Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email", "Failure to create room": "Não foi possível criar a sala", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s", - "Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa.", "%(senderName)s invited %(targetName)s.": "%(senderName)s convidou %(targetName)s.", "%(displayName)s is typing": "%(displayName)s está escrevendo", "%(targetName)s joined the room.": "%(targetName)s entrou na sala.", @@ -392,15 +392,15 @@ "Use with caution": "Use com cautela", "VoIP is unsupported": "Chamada de voz não permitida", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s desfez o convite a %(targetName)s's.", - "You are already in a call": "Você já está em uma chamada", + "You are already in a call.": "Você já está em uma chamada.", "You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione", "You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s", - "You cannot place a call with yourself": "Você não pode iniciar uma chamada", - "You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador", + "You cannot place a call with yourself.": "Você não pode iniciar uma chamada.", + "You cannot place VoIP calls in this browser.": "Você não pode fazer chamadas de voz neste navegador.", "You need to be able to invite users to do that.": "Para fazer isso, você tem que ter permissão para convidar outras pessoas.", "You need to be logged in.": "Você tem que estar logado.", "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor.", "Set a display name:": "Defina um nome público para você:", "Upload an avatar:": "Envie uma imagem de perfil para identificar você:", "This server does not support authentication with a phone number.": "Este servidor não permite a autenticação através de números de telefone.", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 3846d5ba03..f987a0cd68 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -193,7 +193,7 @@ "sent an invitation to": "enviou um convite para", "sent a video": "enviou um vídeo", "Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado", - "Server may be unavailable, overloaded, or you hit a bug": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução", + "Server may be unavailable, overloaded, or you hit a bug.": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução.", "Session ID": "Identificador de sessão", "set a profile picture": "colocou uma foto de perfil", "set their display name to": "configurou seu nome para", @@ -324,8 +324,8 @@ "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s.", "click to reveal": "clique para ver", - "Conference call failed": "Chamada de conferência falhou", - "Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar", + "Conference call failed.": "Chamada de conferência falhou.", + "Conference calling is in development and may not be reliable.": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar.", "Conference calls are not supported in encrypted rooms": "Chamadas de conferência não são possíveis em salas criptografadas", "Conference calls are not supported in this client": "Chamadas de conferência não são possíveis neste navegador", "/ddg is not a command": "/ddg não é um comando", @@ -340,7 +340,7 @@ "Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email", "Failure to create room": "Não foi possível criar a sala", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s", - "Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa.", "%(senderName)s invited %(targetName)s.": "%(senderName)s convidou %(targetName)s.", "%(displayName)s is typing": "%(displayName)s está escrevendo", "%(targetName)s joined the room.": "%(targetName)s entrou na sala.", @@ -397,15 +397,15 @@ "Use with caution": "Use com cautela", "VoIP is unsupported": "Chamada de voz não permitida", "%(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 are already in a call.": "Você já está em uma chamada.", "You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione", "You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s", - "You cannot place a call with yourself": "Você não pode iniciar uma chamada", - "You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador", + "You cannot place a call with yourself.": "Você não pode iniciar uma chamada.", + "You cannot place VoIP calls in this browser.": "Você não pode fazer chamadas de voz neste navegador.", "You need to be able to invite users to do that.": "Para fazer isso, você tem que ter permissão para convidar outras pessoas.", "You need to be logged in.": "Você tem que estar logado.", "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor.", "Set a display name:": "Defina um nome público para você:", "Upload an avatar:": "Envie uma imagem de perfil para identificar você:", "This server does not support authentication with a phone number.": "Este servidor não permite a autenticação através de números de telefone.", @@ -626,7 +626,7 @@ "Server error": "Erro no servidor", "Server may be unavailable, overloaded, or search timed out :(": "O servidor pode estar indisponível, sobrecarregado, ou a busca ultrapassou o tempo limite :(", "Server may be unavailable, overloaded, or the file too big": "O servidor pode estar indisponível, sobrecarregado, ou o arquivo é muito grande", - "Server unavailable, overloaded, or something else went wrong": "O servidor pode estar indisponível, sobrecarregado, ou alguma outra coisa não funcionou", + "Server unavailable, overloaded, or something else went wrong.": "O servidor pode estar indisponível, sobrecarregado, ou alguma outra coisa não funcionou.", "Some of your messages have not been sent": "Algumas das suas mensagens não foram enviadas", "Submit": "Enviar", "The main address for this room is": "O endereço principal desta sala é", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 1ae4719402..30672aeab1 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -232,8 +232,8 @@ "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s уровень мощности изменен на %(powerLevelDiffText)s.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s имя комнаты измененно на %(roomName)s.", "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s измененная тема на %(topic)s.", - "Conference call failed": "Конференц-вызов прервался", - "Conference calling is in development and may not be reliable": "Конференц-вызов находится в процессе и может не быть надежным", + "Conference call failed.": "Конференц-вызов прервался.", + "Conference calling is in development and may not be reliable.": "Конференц-вызов находится в процессе и может не быть надежным.", "Conference calls are not supported in encrypted rooms": "Конференц-вызовы не поддерживаются в зашифрованных комнатах", "Conference calls are not supported in this client": "Конференц-вызовы не поддерживаются в этом клиенте", "/ddg is not a command": "/ddg не команда", @@ -247,7 +247,7 @@ "Failed to verify email address: make sure you clicked the link in the email": "Не удалось подтвердить email-адрес: убедитесь что вы щелкнули по ссылке электронной почты", "Failure to create room": "Не удалось создать комнату", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s из %(fromPowerLevel)s до %(toPowerLevel)s", - "Guest users can't create new rooms. Please register to create room and start a chat": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата.", "click to reveal": "нажать для открытия", "%(senderName)s invited %(targetName)s.": "%(senderName)s приглашает %(targetName)s.", "%(displayName)s is typing": "%(displayName)s вводит текст", @@ -359,11 +359,11 @@ "Upload an avatar:": "Загрузить аватар", "You need to be logged in.": "Вы должны быть зарегистрированы", "You need to be able to invite users to do that.": "Вам необходимо пригласить пользователей чтобы сделать это.", - "You cannot place VoIP calls in this browser": "Вы не можете сделать вызовы VoIP с этим браузером", - "You are already in a call": "Вы уже находитесь в разговоре", + "You cannot place VoIP calls in this browser.": "Вы не можете сделать вызовы VoIP с этим браузером.", + "You are already in a call.": "Вы уже находитесь в разговоре.", "You're not in any rooms yet! Press": "Вы еще не находитесь ни в каких комнатах! Нажать", "You are trying to access %(roomName)s": "Вы пытаетесь получить доступ %(roomName)s", - "You cannot place a call with yourself": "Вы не можете позвонить самим себе", + "You cannot place a call with yourself.": "Вы не можете позвонить самим себе.", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s анулировал %(targetName)s's преглашение.", "Sep": "Сен.", "Jan": "Янв.", @@ -386,7 +386,7 @@ "Fri": "Пя", "Sat": "Сб", "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience.": "Вам необходимо снова войти в генерировать сквозное шифрование (е2е) ключей для этого устройства и предоставить публичный ключ Вашему домашнему серверу. Это после выключения; приносим извинения за причиненные неудобства.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Ваш адрес электронной почты, кажется, не связан с Matrix ID на этом Homeserver", + "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.": "Для его использования, просто подождите результатов автозаполнения для загрузки на вкладке и через них.", diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 91adec837f..54a6886b35 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -30,7 +30,7 @@ "Enable encryption": "启用加密", "Encrypted messages will not be visible on clients that do not yet implement encryption": "不支持加密的客户端将看不到加密的消息", "Encrypted room": "加密聊天室", - "%(senderName)s ended the call.": "%(senderName)s 结束了通话。", + "%(senderName)s ended the call.": "%(senderName)s 结束了通话。.", "End-to-end encryption information": "端到端加密信息", "End-to-end encryption is in beta and may not be reliable": "端到端加密现为测试版,不一定可靠", "Enter Code": "输入代码", @@ -55,7 +55,7 @@ "Failed to save settings": "保存设置失败", "Failed to send email": "发送邮件失败", "Failed to send request.": "发送请求失败。", - "Failed to set avatar.": "设置头像失败。", + "Failed to set avatar.": "设置头像失败。.", "Failed to set display name": "设置昵称失败", "Failed to set up conference call": "无法启动群组通话", "Failed to toggle moderator status": "无法切换管理员权限", @@ -70,15 +70,15 @@ "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.": "出于安全考虑,此会话已被注销。请重新登录。.", "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.": "出于安全考虑,用户注销时会清除浏览器里的端到端加密密钥。如果你想要下次登录 Riot 时能解密过去的聊天记录,请导出你的聊天室密钥。", "Found a bug?": "发现漏洞?", "%(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": "游客不能创建聊天室。请注册以创建聊天室和聊天", + "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": "游客不能上传文件。请注册以上传文件", - "Guests can't use labs features. Please register.": "游客不能使用实验性功能。请注册。", - "Guests cannot join this room even if explicitly invited.": "游客不能加入此聊天室,即使有人主动邀请。", + "Guests can't use labs features. Please register.": "游客不能使用实验性功能。请注册。.", + "Guests cannot join this room even if explicitly invited.": "游客不能加入此聊天室,即使有人主动邀请。.", "had": "已经", "Hangup": "挂断", "Hide read receipts": "隐藏已读回执", @@ -95,7 +95,7 @@ "Invalid Email Address": "邮箱地址格式错误", "Invalid file%(extra)s": "非法文件%(extra)s", "Report it": "报告", - "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "重设密码会导致所有设备上的端到端加密密钥被重置,使得加密的聊天记录不可读,除非你事先导出密钥,修改密码后再导入。此问题将来会得到改善。", + "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "重设密码会导致所有设备上的端到端加密密钥被重置,使得加密的聊天记录不可读,除非你事先导出密钥,修改密码后再导入。此问题将来会得到改善。.", "restore": "恢复", "Return to app": "返回 App", "Return to login screen": "返回登录页面", @@ -117,18 +117,18 @@ "Send Invites": "发送邀请", "Send Reset Email": "发送密码重设邮件", "sent an image": "发了一张图片", - "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s 发了一张图片。", - "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s 向 %(targetDisplayName)s 发了加入聊天室的邀请。", + "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s 发了一张图片。.", + "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s 向 %(targetDisplayName)s 发了加入聊天室的邀请。.", "sent a video": "发了一个视频", "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": "服务器可能不可用、超载,或者其他东西出错了", + "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。", + "%(senderName)s set a profile picture.": "%(senderName)s 设置了头像。.", + "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s 将昵称改为了 %(displayName)s。.", "Settings": "设置", "Show panel": "显示侧边栏", "Show timestamps in 12 hour format (e.g. 2:30pm)": "用12小时制显示时间戳 (如:下午 2:30)", @@ -183,8 +183,8 @@ "%(names)s and %(lastPerson)s are typing": "%(names)s 和 %(lastPerson)s 正在打字", "%(names)s and %(count)s others are typing": "%(names)s 和另外 %(count)s 个人正在打字", "An email has been sent to": "一封邮件已经被发送到", - "A new password must be entered.": "一个新的密码必须被输入。", - "%(senderName)s answered the call.": "%(senderName)s 接了通话。", + "A new password must be entered.": "一个新的密码必须被输入。.", + "%(senderName)s answered the call.": "%(senderName)s 接了通话。.", "An error has occurred.": "一个错误出现了。", "Attachment": "附件", "Autoplay GIFs and videos": "自动播放GIF和视频", @@ -200,9 +200,9 @@ "Invite new room members": "邀请新的聊天室成员", "Join Room": "加入聊天室", "joined": "加入了", - "%(targetName)s joined the room.": "%(targetName)s 加入了聊天室。", + "%(targetName)s joined the room.": "%(targetName)s 加入了聊天室。.", "Jump to first unread message.": "跳到第一条未读消息。", - "%(senderName)s kicked %(targetName)s.": "%(senderName)s 把 %(targetName)s 踢出了聊天室。", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s 把 %(targetName)s 踢出了聊天室。.", "Leave room": "离开聊天室", "Login as guest": "以游客的身份登录", "New password": "新密码" diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index f98bf452af..5238e647b1 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -1,6 +1,6 @@ { "An email has been sent to": "電郵已經發送至", - "A new password must be entered.": "必須輸入新密碼。", + "A new password must be entered.": "必須輸入新密碼。.", "anyone": "任何人", "An error has occurred.": "發生了一個錯誤。", "Anyone who knows the room's link, apart from guests": "任何知道房間連結的人,但訪客除外", @@ -10,7 +10,7 @@ "Are you sure you want to upload the following files?": "您確認要上傳以下文件嗎?", "Attachment": "附件", "Autoplay GIFs and videos": "自動播放GIF和影片", - "%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s。", + "%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s。.", "Ban": "封禁", "Banned users": "已被封禁的使用者", "Blacklisted": "已列入黑名單", @@ -20,5 +20,5 @@ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "當瀏覽器網址列里有 HTTPS URL 時,不能使用 HTTP 連結主伺服器。請採用 HTTPS 或者 允許不安全的腳本", "Can't load user settings": "無法載入使用者設定", "Change Password": "變更密碼", - "%(targetName)s left the room.": "%(targetName)s 離開了聊天室。" + "%(targetName)s left the room.": "%(targetName)s 離開了聊天室。." } From 0b6d20a62ef523e0155f666bdfc0acb909609428 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Thu, 1 Jun 2017 20:46:07 +0000 Subject: [PATCH 153/416] Translated using Weblate (German) Currently translated at 99.6% (778 of 781 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 | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 00de1617a1..8dc0cdd9a7 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -64,7 +64,7 @@ "Anyone who knows the room's link, including guests": "Jeder der den Raum-Link kennt - auch Gäste", "Are you sure you want to leave the room?": "Bist du sicher, dass du den Raum verlassen willst?", "Are you sure you want to reject the invitation?": "Bist du sicher, dass die die Einladung ablehnen willst?", - "Are you sure you want to upload the following files?": "Bist du sicher, dass du die folgenden Dateien hochladen willst?", + "Are you sure you want to upload the following files?": "Bist du sicher, dass du die folgenden Dateien hochladen möchtest?", "banned": "gebannt", "Banned users": "Gebannte Nutzer", "Bug Report": "Fehlerbericht", @@ -93,7 +93,7 @@ "Encryption is enabled in this room": "Verschlüsselung ist in diesem Raum aktiviert", "Encryption is not enabled in this room": "Verschlüsselung ist in diesem Raum nicht aktiviert", "ended the call.": "beendete den Anruf.", - "End-to-end encryption is in beta and may not be reliable": "Die Ende-zu-Ende-Verschlüsselung befindet sich im Beta-Stadium und ist eventuell nicht hundertprozentig zuverlässig", + "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", @@ -117,7 +117,7 @@ "Failed to reject invitation": "Fehler beim Abweisen der Einladung", "Failed to set avatar.": "Fehler beim Setzen des Avatars.", "Failed to unban": "Entbannen fehlgeschlagen", - "Failed to upload file": "Dateiupload fehlgeschlagen", + "Failed to upload file": "Datei-Upload fehlgeschlagen", "Favourite": "Favorit", "favourite": "Favoriten", "Forget room": "Raum vergessen", @@ -152,7 +152,7 @@ "made future room history visible to": "mache kommende Raum-Historie sichtbar für", "Manage Integrations": "Integrationen verwalten", "Members only": "Nur Mitglieder", - "Mobile phone number": "Mobile Telefonnummer", + "Mobile phone number": "Mobiltelefonnummer", "Moderator": "Moderator", "my Matrix ID": "Meine Matrix-ID", "Never send encrypted messages to unverified devices from this device": "Niemals verschlüsselte Nachrichten an unverifizierte Geräte von diesem Gerät aus versenden", @@ -164,7 +164,7 @@ "No users have specific privileges in this room": "Kein Benutzer hat in diesem Raum besondere Berechtigungen", "olm version": "OLM-Version", "Once encryption is enabled for a room it cannot be turned off again (for now)": "Sobald Verschlüsselung für einen Raum aktiviert wird, kann diese (aktuell noch) nicht wieder deaktiviert werden", - "Only people who have been invited": "Nur Personen die eingeladen wurden", + "Only people who have been invited": "Nur Personen, die eingeladen wurden", "or": "oder", "other": "weiteres", "others": "andere", @@ -197,7 +197,7 @@ "sent an image": "sandte ein Bild", "sent an invitation to": "sandte eine Einladung an", "sent a video": "sandte ein Video", - "Server may be unavailable or overloaded": "Server könnte nicht verfügbar oder überlastet sein", + "Server may be unavailable or overloaded": "Server ist eventuell nicht verfügbar oder überlastet", "set a profile picture": "setzte ein Profilbild", "set their display name to": "setzte den Anzeigenamen auf", "Settings": "Einstellungen", @@ -215,7 +215,7 @@ "their invitations": "ihre Einladungen", "their invitation": "ihre Einladung", "These are experimental features that may break in unexpected ways. Use with caution": "Dies sind experimentelle Funktionen die in unerwarteter Weise Fehler verursachen können. Mit Vorsicht benutzen", - "The visibility of existing history will be unchanged": "Die Sichtbarkeit der existenten Historie bleibt unverändert", + "The visibility of existing history will be unchanged": "Die Sichtbarkeit der bereits vorhandenen Chat-Historie bleibt unverändert", "This doesn't appear to be a valid email address": "Dies scheint keine gültige E-Mail-Adresse zu sein", "this invitation?": "diese Einladung?", "This is a preview of this room. Room interactions have been disabled": "Dies ist eine Vorschau dieses Raumes. Raum-Interaktionen wurden deaktiviert", @@ -284,7 +284,7 @@ "Conference calling is in development and may not be reliable": "Konferenzgespräche sind in Entwicklung und evtl. nicht zuverlässig", "Conference calls are not supported in encrypted rooms": "Konferenzgespräche sind in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche sind in diesem Client nicht unterstützt", - "Existing Call": "Existierender Anruf", + "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", @@ -342,7 +342,7 @@ "An error occured: %(error_string)s": "Ein Fehler trat auf: %(error_string)s", "Topic": "Thema", "Make this room private": "Mache diesen Raum privat", - "Share message history with new users": "Teile Nachrichtenhistorie mit neuen Nutzern", + "Share message history with new users": "Nachrichtenhistorie mit neuen Nutzern teilen", "Encrypt room": "Raum verschlüsseln", "To send events of type": "Zum Senden von Ereignissen mit Typ", "%(names)s and %(lastPerson)s are typing": "%(names)s und %(lastPerson)s schreiben", @@ -353,17 +353,17 @@ "%(senderName)s answered the call.": "%(senderName)s beantwortete den Anruf.", "%(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 profile picture.": "%(senderName)s änderte das Profilbild.", + "%(senderName)s changed their profile picture.": "%(senderName)s hat das Profilbild geändert.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s.", - "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s änderte das Thema zu \"%(topic)s\".", + "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s hat das Thema geändert in \"%(topic)s\".", "/ddg is not a command": "/ddg ist kein Kommando", "%(senderName)s ended the call.": "%(senderName)s hat den Anruf beendet.", "Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl", "Failed to send request.": "Anfrage zu senden schlug fehl.", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s", - "%(senderName)s invited %(targetName)s.": "%(senderName)s lud %(targetName)s ein.", - "%(displayName)s is typing": "%(displayName)s tippt", + "%(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.", "%(senderName)s kicked %(targetName)s.": "%(senderName)s kickte %(targetName)s.", "%(targetName)s left the room.": "%(targetName)s verließ den Raum.", @@ -372,14 +372,14 @@ "Missing user_id in request": "Fehlende user_id in Anfrage", "Must be viewing a room": "Muss einen Raum ansehen", "New Composer & Autocomplete": "Neuer Eingabeverarbeiter & Autovervollständigung", - "(not supported by this browser)": "(nicht von diesem Browser unterstützt)", + "(not supported by this browser)": "(wird von diesem Browser nicht unterstützt)", "%(senderName)s placed a %(callType)s call.": "%(senderName)s startete einen %(callType)s-Anruf.", "Power level must be positive integer.": "Berechtigungslevel muss eine positive ganze Zahl sein.", "Reason": "Grund", - "%(targetName)s rejected the invitation.": "%(targetName)s lehnte die Einladung ab.", + "%(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 requested a VoIP conference.": "%(senderName)s fragte nach einer VoIP-Konferenz.", + "%(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 sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten.", @@ -466,7 +466,7 @@ "es-ar": "Spanisch (Argentinien)", "es-bo": "Spanisch (Bolivien)", "es-cl": "Spanisch (Chile)", - "es-co": "Spanisch (Kolombien)", + "es-co": "Spanisch (Kolumbien)", "es-cr": "Spanisch (Costa Rica)", "es-do": "Spanisch (Dominikanische Republik)", "es-ec": "Spanisch (Ecuador)", @@ -583,7 +583,7 @@ "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 Verifizierungsscode", + "Incorrect verification code": "Falscher Verifizierungscode", "Invalid alias format": "Ungültiges Alias-Format", "Invalid address format": "Ungültiges Adressformat", "'%(alias)s' is not a valid format for an address": "'%(alias)s' ist kein gültiges Adressformat", @@ -639,14 +639,14 @@ "quote": "Zitat", "bullet": "Aufzählung", "Click to unmute video": "Klicken, um die Video-Stummschaltung zu deaktivieren", - "Click to unmute audio": "Klicke um Ton zu reaktivieren", + "Click to unmute audio": "Klicken, um den Ton wieder einzuschalten", "Failed to load timeline position": "Laden der Position im Zeitstrahl fehlgeschlagen", "Failed to toggle moderator status": "Umschalten des Moderator-Status fehlgeschlagen", "Enable encryption": "Verschlüsselung aktivieren", "The main address for this room is": "Die Hauptadresse für diesen Raum ist", "Autoplay GIFs and videos": "GIF-Dateien und Videos automatisch abspielen", - "Don't send typing notifications": "Nicht senden, wenn ich tippe", - "Hide read receipts": "Verberge Lesebestätigungen", + "Don't send typing notifications": "Schreibbenachrichtigungen unterdrücken", + "Hide read receipts": "Lesebestätigungen verbergen", "Never send encrypted messages to unverified devices in this room": "In diesem Raum keine verschlüsselten Nachrichten an unverifizierte Geräte senden", "numbullet": "Nummerierung", "%(items)s and %(remaining)s others": "%(items)s und %(remaining)s weitere", @@ -697,10 +697,10 @@ "%(severalUsers)schanged their avatar": "%(severalUsers)sänderten ihre Avatare", "%(oneUser)schanged their avatar": "%(oneUser)sänderte seinen/ihren Avatar", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s %(time)s", - "%(oneUser)sleft and rejoined": "%(oneUser)s ging und trat erneut bei", + "%(oneUser)sleft and rejoined": "%(oneUser)sverließ den Raum und trat erneut bei", "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": "Immer Nachrichten-Zeitstempel anzeigen", + "Always show message timestamps": "Nachrichten-Zeitstempel immer anzeigen", "Authentication": "Authentifikation", "An error has occurred.": "Ein Fehler passierte.", "Confirm password": "Passwort bestätigen", @@ -712,7 +712,7 @@ "New passwords don't match": "Die neuen Passwörter stimmen nicht überein", "olm version:": "Version von olm:", "Passwords can't be empty": "Passwortfelder dürfen nicht leer sein", - "Registration required": "Registrierung benötigt", + "Registration required": "Registrierung erforderlich", "Report it": "Melde ihn", "riot-web version:": "Version von riot-web:", "Scroll to bottom of page": "Zum Ende der Seite springen", @@ -731,13 +731,13 @@ "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s löschte den Raumnamen.", "Passphrases must match": "Passphrase muss übereinstimmen", "Passphrase must not be empty": "Passphrase darf nicht leer sein", - "Export room keys": "Exportiere Raum-Schlüssel", + "Export room keys": "Raum-Schlüssel exportieren", "Enter passphrase": "Passphrase eingeben", "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": "Lehne alle %(invitedRooms)s Einladungen ab", + "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.", "Failed to invite": "Einladen fehlgeschlagen", @@ -760,7 +760,7 @@ "Unknown devices": "Unbekannte Geräte", "Unknown Address": "Unbekannte Adresse", "Verify...": "Verifizieren...", - "ex. @bob:example.com": "z.B. @bob:example.com", + "ex. @bob:example.com": "z. B. @bob:example.com", "Add User": "Nutzer hinzufügen", "Sign in with CAS": "Mit CAS anmelden", "Custom Server Options": "Erweiterte Server-Optionen", @@ -814,7 +814,7 @@ "Unverify": "Entverifizieren", "This Home Server would like to make sure you are not a robot": "Dieser Heimserver möchte sicherstellen, dass du kein Roboter bist", "Drop file here to upload": "Datei hier loslassen zum hochladen", - "Idle": "inaktiv", + "Idle": "Untätig", "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.": "Wir empfehlen dir für jedes Gerät durch den Verifizierungsprozess zu gehen um zu bestätigen, dass sie ihrem legitimierten Besitzer gehören, aber du kannst die Nachrichten ohne Verifizierung erneut senden, wenn du es vorziehst.", "Ongoing conference call%(supportedText)s. %(joinText)s": "Laufendes Konferenzgespräch%(supportedText)s. %(joinText)s", "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?": "Du wirst jetzt auf die Website eines Drittanbieters weitergeleitet, damit du dein Konto für die Verwendung von %(integrationsUrl)s authentifizieren kannst. Möchtest du fortfahren?", From 10095af4d91fca5f5cba36d9bfd757f22e57f79e Mon Sep 17 00:00:00 2001 From: Amandine Date: Thu, 1 Jun 2017 21:41:45 +0000 Subject: [PATCH 154/416] Translated using Weblate (French) Currently translated at 100.0% (797 of 797 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 | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index fbcc04e881..3b4c754965 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -444,7 +444,7 @@ "Server may be unavailable or overloaded": "Le serveur semble être inaccessible ou surchargé", "Server may be unavailable, overloaded, or search timed out :(": "Le serveur semble être inaccessible, surchargé ou la recherche a expiré :(", "Server may be unavailable, overloaded, or the file too big": "Le serveur semble être inaccessible, surchargé ou le fichier trop important", - "Server may be unavailable, overloaded, or you hit a bug.": "Le serveur semble être inaccessible, surchargé ou vous avez rencontré un problème.", + "Server may be unavailable, overloaded, or you hit a bug.": "Le serveur semble être indisponible, surchargé, ou vous avez rencontré un problème.", "Server unavailable, overloaded, or something else went wrong.": "Le serveur semble être inaccessible, surchargé ou quelque chose s'est mal passé.", "Session ID": "Identifiant de session", "%(senderName)s set a profile picture.": "%(senderName)s a défini une photo de profil.", @@ -693,7 +693,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.": "Ce processus vous permet d’importer les clés d’encryption que vous avez précédemment exportées depuis un autre client Matrix. Vous serez alors capable de décrypter n’importe quel messages que l’autre client peut décrypter.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Le fichier exporté est protégé par une phrase secrète. Vous devez entrer cette phrase secrète ici pour décrypter le fichier.", "You must join the room to see its files": "Vous devez joindre le salon pour voir ses fichiers", - "Server may be unavailable, overloaded, or you hit a bug.": "Le serveur semble être indisponible, surchargé, ou vous avez rencontré un problème.", "Reject all %(invitedRooms)s invites": "Rejeter la totalité des %(invitedRooms)s invitations", "Start new Chat": "Démarrer une nouvelle conversation", "Guest users can't invite users. Please register.": "Les visiteurs ne peuvent inviter d’autres utilisateurs. Merci de vous enregistrer.", @@ -781,5 +780,22 @@ "for %(amount)sd": "depuis %(amount)sj", "$senderDisplayName changed the room avatar to ": "$senderDisplayName a changé l’image de profil du salon en ", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s a supprimé l’image de profil du salon.", - "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s a changé l’image de profil de %(roomName)s" + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s a changé l’image de profil de %(roomName)s", + "Device already verified!": "Appareil déjà vérifié !", + "Export": "Exporter", + "Failed to register as guest:": "Échec de l’inscription en tant que visiteur :", + "Guest access is disabled on this Home Server.": "L’accès en tant que visiteur est désactivé sur ce serveur.", + "Import": "Importer", + "Incorrect username and/or password.": "Nom d’utilisateur et/ou mot de passe incorrect.", + "Results from DuckDuckGo": "Résultats de DuckDuckGo", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Les clés de signature que vous avez transmises correspondent aux clés que vous avez reçues de l’appareil %(deviceId)s de %(userId)s. L’appareil est vérifié.", + "This Home Server does not support login using email address.": "Ce serveur ne supporte pas l’identification par e-mail.", + "There was a problem logging in.": "Un problème a été rencontré lors de l’identification.", + "Unknown (user, device) pair:": "Couple (utilisateur, appareil) inconnu :", + "Unrecognised command:": "Commande non-reconnue :", + "Unrecognised room alias:": "Alias de salon non-reconnu :", + "Use compact timeline layout": "Utiliser l'affichage compact", + "Verified key": "Clé vérifiée", + "WARNING: Device already verified, but keys do NOT MATCH!": "ATTENTION : Appareil déjà vérifié mais les clés NE CORRESPONDENT PAS !", + "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!": "ATTENTION : ERREUR DE VÉRIFICATION DES CLÉS ! La clé de signature pour %(userId)s et l'appareil %(deviceId)s est “%(fprint)s” et ne correspond pas à la clé “%(fingerprint)s” qui a été fournie. Cela peut signifier que vos communications sont interceptées !" } From dbba1dedb6a529ce1198f2b2d85ec8e7b046c981 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 1 Jun 2017 22:58:17 +0100 Subject: [PATCH 155/416] i18nize all the things and change show logic Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 22 ++++++++++------------ src/i18n/strings/en_EN.json | 6 ++++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 4cb49d8c1e..b33bdd271d 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -902,9 +902,7 @@ module.exports = React.createClass({ }, _mapWebRtcDevicesToSpans: function(devices) { - return Object.keys(devices).map( - (deviceId) => {devices[deviceId]} - ); + return Object.keys(devices).map((deviceId) => {devices[deviceId]}); }, _setAudioInput: function(deviceId) { @@ -928,8 +926,8 @@ module.exports = React.createClass({ function() { const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createDialog(ErrorDialog, { - title: "No media permissions", - description: "You may need to manually permit Riot to access your microphone/webcam", + title: _t('No media permissions'), + description: _t('You may need to manually permit Riot to access your microphone/webcam'), }); }, ]); @@ -939,10 +937,10 @@ module.exports = React.createClass({ _renderWebRtcSettings: function() { if (this.state.mediaDevices === false) { return
    -

    WebRTC

    +

    {_t('VoIP')}

    - Missing Media Permissions, click here to request. + {_t('Missing Media Permissions, click here to request.')}

    ; @@ -950,11 +948,11 @@ module.exports = React.createClass({ const Dropdown = sdk.getComponent('elements.Dropdown'); - let microphoneDropdown =

    No Microphones detected

    ; - let webcamDropdown =

    No Webcams detected

    ; + let microphoneDropdown =

    {_t('No Microphones detected')}

    ; + let webcamDropdown =

    {_t('No Webcams detected')}

    ; const audioInputs = this.state.mediaDevices.audioinput; - if ('default' in audioInputs) { + if (Object.keys(videoInputs).length > 0) { microphoneDropdown =

    Microphone

    0) { webcamDropdown =

    Cameras

    -

    WebRTC

    +

    {_t('VoIP')}

    {microphoneDropdown} {webcamDropdown} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 211d164c2a..44a1af57fa 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -129,6 +129,12 @@ "Add email address": "Add email address", "Add phone number": "Add phone number", "Admin": "Admin", + "VoIP": "VoIP", + "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", + "No Microphones detected": "No Microphones detected", + "No Webcams detected": "No Webcams detected", + "No media permissions": "No media permissions", + "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", "Advanced": "Advanced", "Algorithm": "Algorithm", "Always show message timestamps": "Always show message timestamps", From 0c367783691629c551cea9ec02e1cd4fa6f5ccbe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 1 Jun 2017 22:59:37 +0100 Subject: [PATCH 156/416] fix bad indentation Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 44a1af57fa..dcf3670dd9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -134,7 +134,7 @@ "No Microphones detected": "No Microphones detected", "No Webcams detected": "No Webcams detected", "No media permissions": "No media permissions", - "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", + "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", "Advanced": "Advanced", "Algorithm": "Algorithm", "Always show message timestamps": "Always show message timestamps", From aa90d6b097d4b541b1d260f4655482d4c2d117fb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 1 Jun 2017 23:00:25 +0100 Subject: [PATCH 157/416] fix **AMAZING** C&P derp Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index b33bdd271d..f660cf71e1 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -952,7 +952,7 @@ module.exports = React.createClass({ let webcamDropdown =

    {_t('No Webcams detected')}

    ; const audioInputs = this.state.mediaDevices.audioinput; - if (Object.keys(videoInputs).length > 0) { + if (Object.keys(audioInputs).length > 0) { microphoneDropdown =

    Microphone

    Date: Thu, 1 Jun 2017 23:25:44 +0100 Subject: [PATCH 158/416] change device data structure to array of objects so that we can set falsey values, for unsetting device most dolphinately needs testing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/CallMediaHandler.js | 8 ++++---- src/components/structures/UserSettings.js | 17 ++++++++++++----- src/i18n/strings/en_EN.json | 1 + 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 4f82e003b9..45ca5dc30d 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -23,15 +23,15 @@ export default { // 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 = {}; + const audioIn = []; + const videoIn = []; if (devices.some((device) => !device.label)) return false; devices.forEach((device) => { switch (device.kind) { - case 'audioinput': audioIn[device.deviceId] = device.label; break; - case 'videoinput': videoIn[device.deviceId] = device.label; break; + case 'audioinput': audioIn.push(device); break; + case 'videoinput': videoIn.push(device); break; } }); diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index f660cf71e1..fcb7a70559 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -269,8 +269,8 @@ module.exports = React.createClass({ if (this._unmounted) return; this.setState({ mediaDevices, - activeAudioInput: this._localSettings['webrtc_audioinput'] || 'default', - activeVideoInput: this._localSettings['webrtc_videoinput'] || 'default', + activeAudioInput: this._localSettings['webrtc_audioinput'], + activeVideoInput: this._localSettings['webrtc_videoinput'], }); }); }, @@ -902,7 +902,7 @@ module.exports = React.createClass({ }, _mapWebRtcDevicesToSpans: function(devices) { - return Object.keys(devices).map((deviceId) => {devices[deviceId]}); + return devices.map((device) => {devices[device.deviceId]}); }, _setAudioInput: function(deviceId) { @@ -951,8 +951,14 @@ module.exports = React.createClass({ let microphoneDropdown =

    {_t('No Microphones detected')}

    ; let webcamDropdown =

    {_t('No Webcams detected')}

    ; + const defaultOption = { + deviceId: undefined, + label: _t('Default Device'), + }; + const audioInputs = this.state.mediaDevices.audioinput; - if (Object.keys(audioInputs).length > 0) { + if (audioInputs.length > 0) { + audioInputs.unshift(defaultOption); microphoneDropdown =

    Microphone

    0) { + if (videoInputs.length > 0) { + videoInputs.unshift(defaultOption); webcamDropdown =

    Cameras

    Date: Thu, 1 Jun 2017 23:33:36 +0100 Subject: [PATCH 159/416] only unshift default if there is no deviceId===default Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index fcb7a70559..5dd6a7fec2 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -958,7 +958,9 @@ module.exports = React.createClass({ const audioInputs = this.state.mediaDevices.audioinput; if (audioInputs.length > 0) { - audioInputs.unshift(defaultOption); + if (!audioInputs.some((input) => input.deviceId === 'default')) { + audioInputs.unshift(defaultOption); + } microphoneDropdown =

    Microphone

    0) { - videoInputs.unshift(defaultOption); + if (!videoInputs.some((input) => input.deviceId === 'default')) { + videoInputs.unshift(defaultOption); + } webcamDropdown =

    Cameras

    Date: Thu, 1 Jun 2017 23:39:54 +0100 Subject: [PATCH 160/416] lets actually make things work, eh? Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 5dd6a7fec2..9a55094edf 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -902,7 +902,7 @@ module.exports = React.createClass({ }, _mapWebRtcDevicesToSpans: function(devices) { - return devices.map((device) => {devices[device.deviceId]}); + return devices.map((device) => {device.label}); }, _setAudioInput: function(deviceId) { From beedeec1636e0d43938b0edeaf688a98b78117ff Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 1 Jun 2017 23:50:14 +0100 Subject: [PATCH 161/416] copy the arrays so we're not making a mess Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 9a55094edf..b5d5c23296 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -956,7 +956,7 @@ module.exports = React.createClass({ label: _t('Default Device'), }; - const audioInputs = this.state.mediaDevices.audioinput; + const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { if (!audioInputs.some((input) => input.deviceId === 'default')) { audioInputs.unshift(defaultOption); @@ -972,7 +972,7 @@ module.exports = React.createClass({
    ; } - const videoInputs = this.state.mediaDevices.videoinput; + const videoInputs = this.state.mediaDevices.videoinput.slice(0); if (videoInputs.length > 0) { if (!videoInputs.some((input) => input.deviceId === 'default')) { videoInputs.unshift(defaultOption); From 3eb519b2275de34461edb67c99ab1af493b96c33 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 1 Jun 2017 23:54:17 +0100 Subject: [PATCH 162/416] this is just endless Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index b5d5c23296..b8567fc180 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -958,14 +958,18 @@ module.exports = React.createClass({ const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { + let defaultInput; if (!audioInputs.some((input) => input.deviceId === 'default')) { audioInputs.unshift(defaultOption); + } else { + defaultInput = 'default'; } + microphoneDropdown =

    Microphone

    {this._mapWebRtcDevicesToSpans(audioInputs)} @@ -974,14 +978,18 @@ module.exports = React.createClass({ const videoInputs = this.state.mediaDevices.videoinput.slice(0); if (videoInputs.length > 0) { + let defaultInput; if (!videoInputs.some((input) => input.deviceId === 'default')) { videoInputs.unshift(defaultOption); + } else { + defaultInput = 'default'; } + webcamDropdown =

    Cameras

    {this._mapWebRtcDevicesToSpans(videoInputs)} From b411c8f97e88f79e37e52adc9700db216cb8642f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 2 Jun 2017 00:10:54 +0100 Subject: [PATCH 163/416] shorten signin label to aid fr i18n --- src/i18n/strings/de_DE.json | 2 +- src/i18n/strings/en_EN.json | 2 +- src/i18n/strings/es.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 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index baf2b317cf..cd3d82b112 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -139,7 +139,7 @@ "Invite new room members": "Lade neue Raum-Mitglieder ein", "is a": "ist ein", "is trusted": "wird vertraut", - "I want to sign in with": "Ich möchte mich anmelden mit", + "Sign in with": "Ich möchte mich anmelden mit", "joined and left": "trat bei und ging", "joined": "trat bei", "joined the room": "trat dem Raum bei", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 211d164c2a..5d6a010638 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -318,7 +318,7 @@ "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", "%(displayName)s is typing": "%(displayName)s is typing", - "I want to sign in with": "I want to sign in with", + "Sign in with": "Sign in with", "Join Room": "Join Room", "joined and left": "joined and left", "joined": "joined", diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index e071e63a3a..df6f0dd011 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -308,7 +308,7 @@ "'%(alias)s' is not a valid format for an address": "'%(alias)s' no es un formato válido para una dirección", "'%(alias)s' is not a valid format for an alias": "'%(alias)s' no es un formato válido para un alias", "%(displayName)s is typing": "%(displayName)s esta escribiendo", - "I want to sign in with": "Quiero iniciar sesión con", + "Sign in with": "Quiero iniciar sesión con", "Join Room": "Unirte a la sala", "joined and left": "unido y dejado", "joined": "unido", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index fbcc04e881..a2c008c00d 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -318,7 +318,7 @@ "'%(alias)s' is not a valid format for an address": "'%(alias)s' n'est pas un format valide pour une adresse", "'%(alias)s' is not a valid format for an alias": "'%(alias)s' n'est pas un format valide pour un alias", "%(displayName)s is typing": "%(displayName)s est en train de taper", - "I want to sign in with": "Je veux m'identifier avec", + "Sign in with": "Je veux m'identifier avec", "Join Room": "Rejoindre le salon", "joined and left": "a joint et quitté", "joined": "a joint", diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index ad4a9774d6..830952bd57 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -112,7 +112,7 @@ "Invites": "Convidar", "Invites user with given id to current room": "Convidar usuários com um dado identificador para esta sala", "is a": "é um(a)", - "I want to sign in with": "Quero entrar", + "Sign in with": "Quero entrar", "joined and left": "entrou e saiu", "joined": "entrou", "joined the room": "entrou na sala", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index f987a0cd68..a44bf0c955 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -115,7 +115,7 @@ "Invites": "Convidar", "Invites user with given id to current room": "Convidar usuários com um dado identificador para esta sala", "is a": "é um(a)", - "I want to sign in with": "Quero entrar", + "Sign in with": "Quero entrar", "joined and left": "entrou e saiu", "joined": "entrou", "joined the room": "entrou na sala", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 30672aeab1..743aade542 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -103,7 +103,7 @@ "Invites": "Приглашать", "Invites user with given id to current room": "Пригласить пользователя с данным id в текущую комнату", "is a": "является", - "I want to sign in with": "Я хочу регистрироваться с", + "Sign in with": "Я хочу регистрироваться с", "joined and left": "присоединенный и оставленный", "joined": "присоединенный", "joined the room": "joined the room", From 6a225d1ac20bcf00275bcc252835632a090516c8 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 2 Jun 2017 00:16:17 +0100 Subject: [PATCH 164/416] use new sign-in string --- src/components/views/login/PasswordLogin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js index 11d8f2cc7c..dff6fb0b58 100644 --- a/src/components/views/login/PasswordLogin.js +++ b/src/components/views/login/PasswordLogin.js @@ -182,7 +182,7 @@ class PasswordLogin extends React.Component {
    - + Date: Fri, 2 Jun 2017 00:20:34 +0100 Subject: [PATCH 165/416] special case default - CallMediaHandler can figure it out Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/CallMediaHandler.js | 24 +++++++++++++++++++++++ src/components/structures/UserSettings.js | 16 +++++---------- src/i18n/strings/en_EN.json | 2 ++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 45ca5dc30d..780df60846 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -53,12 +53,36 @@ export default { // }); }, + _findDefault: function(devices) { + return devices.some((device) => device.deviceId === 'default') ? 'default' : undefined; + }, + + setAudioInputDefault: async function() { + const devices = await this.getDevices(); + const audioDefault = this._findDefault(devices.audioinput); + this._setAudioInput(audioDefault); + }, + setAudioInput: function(deviceId) { + this[deviceId === 'default' ? 'setAudioInputDefault' : '_setAudioInput'](deviceId); + }, + + _setAudioInput: function(deviceId) { UserSettingsStore.setLocalSetting('webrtc_audioinput', deviceId); Matrix.setMatrixCallAudioInput(deviceId); }, + setVideoInputDefault: async function() { + const devices = await this.getDevices(); + const videoDefault = this._findDefault(devices.videoinput); + this._setVideoInput(videoDefault); + }, + setVideoInput: function(deviceId) { + this[deviceId === 'default' ? 'setVideoInputDefault' : '_setVideoInput'](); + }, + + _setVideoInput: function(deviceId) { UserSettingsStore.setLocalSetting('webrtc_videoinput', deviceId); Matrix.setMatrixCallVideoInput(deviceId); }, diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index b8567fc180..99b02b59f9 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -952,24 +952,21 @@ module.exports = React.createClass({ let webcamDropdown =

    {_t('No Webcams detected')}

    ; const defaultOption = { - deviceId: undefined, + deviceId: 'default', label: _t('Default Device'), }; const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { - let defaultInput; if (!audioInputs.some((input) => input.deviceId === 'default')) { audioInputs.unshift(defaultOption); - } else { - defaultInput = 'default'; } microphoneDropdown =
    -

    Microphone

    +

    {_t('Microphone')}

    {this._mapWebRtcDevicesToSpans(audioInputs)} @@ -978,18 +975,15 @@ module.exports = React.createClass({ const videoInputs = this.state.mediaDevices.videoinput.slice(0); if (videoInputs.length > 0) { - let defaultInput; if (!videoInputs.some((input) => input.deviceId === 'default')) { videoInputs.unshift(defaultOption); - } else { - defaultInput = 'default'; } webcamDropdown =
    -

    Cameras

    +

    {_t('Camera')}

    {this._mapWebRtcDevicesToSpans(videoInputs)} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3a8752fd5d..b9ac79d362 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -136,6 +136,8 @@ "No media permissions": "No media permissions", "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", "Default Device": "Default Device", + "Microphone": "Microphone", + "Cameras": "Cameras", "Advanced": "Advanced", "Algorithm": "Algorithm", "Always show message timestamps": "Always show message timestamps", From c7285b905321a12d760a316c49012144a2ef6f2d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 2 Jun 2017 00:21:00 +0100 Subject: [PATCH 166/416] fix fr layout --- src/components/views/login/ServerConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/login/ServerConfig.js b/src/components/views/login/ServerConfig.js index 3abe64cd2f..a63d02416c 100644 --- a/src/components/views/login/ServerConfig.js +++ b/src/components/views/login/ServerConfig.js @@ -132,7 +132,7 @@ module.exports = React.createClass({ var toggleButton; if (this.props.withToggleButton) { toggleButton = ( -
    +
    From 4976cbb4240acc32bc2cc88c36c48d2d06a847ec Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 2 Jun 2017 00:21:34 +0100 Subject: [PATCH 167/416] missed a thing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/CallMediaHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 780df60846..d5813c1d5c 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -79,7 +79,7 @@ export default { }, setVideoInput: function(deviceId) { - this[deviceId === 'default' ? 'setVideoInputDefault' : '_setVideoInput'](); + this[deviceId === 'default' ? 'setVideoInputDefault' : '_setVideoInput'](deviceId); }, _setVideoInput: function(deviceId) { From 0bafd6458a0a6a06b8d9f14dff097cffc75e297e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 2 Jun 2017 00:26:31 +0100 Subject: [PATCH 168/416] Revert voodoo --- src/CallMediaHandler.js | 24 ----------------------- src/components/structures/UserSettings.js | 16 ++++++++++----- src/i18n/strings/en_EN.json | 2 -- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index d5813c1d5c..45ca5dc30d 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -53,36 +53,12 @@ export default { // }); }, - _findDefault: function(devices) { - return devices.some((device) => device.deviceId === 'default') ? 'default' : undefined; - }, - - setAudioInputDefault: async function() { - const devices = await this.getDevices(); - const audioDefault = this._findDefault(devices.audioinput); - this._setAudioInput(audioDefault); - }, - setAudioInput: function(deviceId) { - this[deviceId === 'default' ? 'setAudioInputDefault' : '_setAudioInput'](deviceId); - }, - - _setAudioInput: function(deviceId) { UserSettingsStore.setLocalSetting('webrtc_audioinput', deviceId); Matrix.setMatrixCallAudioInput(deviceId); }, - setVideoInputDefault: async function() { - const devices = await this.getDevices(); - const videoDefault = this._findDefault(devices.videoinput); - this._setVideoInput(videoDefault); - }, - setVideoInput: function(deviceId) { - this[deviceId === 'default' ? 'setVideoInputDefault' : '_setVideoInput'](deviceId); - }, - - _setVideoInput: function(deviceId) { UserSettingsStore.setLocalSetting('webrtc_videoinput', deviceId); Matrix.setMatrixCallVideoInput(deviceId); }, diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 99b02b59f9..b8567fc180 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -952,21 +952,24 @@ module.exports = React.createClass({ let webcamDropdown =

    {_t('No Webcams detected')}

    ; const defaultOption = { - deviceId: 'default', + deviceId: undefined, label: _t('Default Device'), }; const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { + let defaultInput; if (!audioInputs.some((input) => input.deviceId === 'default')) { audioInputs.unshift(defaultOption); + } else { + defaultInput = 'default'; } microphoneDropdown =
    -

    {_t('Microphone')}

    +

    Microphone

    {this._mapWebRtcDevicesToSpans(audioInputs)} @@ -975,15 +978,18 @@ module.exports = React.createClass({ const videoInputs = this.state.mediaDevices.videoinput.slice(0); if (videoInputs.length > 0) { + let defaultInput; if (!videoInputs.some((input) => input.deviceId === 'default')) { videoInputs.unshift(defaultOption); + } else { + defaultInput = 'default'; } webcamDropdown =
    -

    {_t('Camera')}

    +

    Cameras

    {this._mapWebRtcDevicesToSpans(videoInputs)} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b9ac79d362..3a8752fd5d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -136,8 +136,6 @@ "No media permissions": "No media permissions", "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", "Default Device": "Default Device", - "Microphone": "Microphone", - "Cameras": "Cameras", "Advanced": "Advanced", "Algorithm": "Algorithm", "Always show message timestamps": "Always show message timestamps", From 6b4daf02a9d6522d695b9b8be440f46be7685df8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 2 Jun 2017 00:27:20 +0100 Subject: [PATCH 169/416] i18 missed things Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 4 ++-- src/i18n/strings/en_EN.json | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index b8567fc180..837cf99f1a 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -966,7 +966,7 @@ module.exports = React.createClass({ } microphoneDropdown =
    -

    Microphone

    +

    {_t('Microphone')}

    -

    Cameras

    +

    {_t('Camera')}

    Date: Fri, 2 Jun 2017 00:31:43 +0100 Subject: [PATCH 170/416] try empty string as falsey key Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 837cf99f1a..389f08fd3b 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -952,7 +952,7 @@ module.exports = React.createClass({ let webcamDropdown =

    {_t('No Webcams detected')}

    ; const defaultOption = { - deviceId: undefined, + deviceId: '', label: _t('Default Device'), }; From b1973d799860535577ee1234277540f2ed3a71b5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 2 Jun 2017 00:42:19 +0100 Subject: [PATCH 171/416] undefined =/= '' Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 389f08fd3b..7300d82541 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -958,7 +958,7 @@ module.exports = React.createClass({ const audioInputs = this.state.mediaDevices.audioinput.slice(0); if (audioInputs.length > 0) { - let defaultInput; + let defaultInput = ''; if (!audioInputs.some((input) => input.deviceId === 'default')) { audioInputs.unshift(defaultOption); } else { @@ -978,7 +978,7 @@ module.exports = React.createClass({ const videoInputs = this.state.mediaDevices.videoinput.slice(0); if (videoInputs.length > 0) { - let defaultInput; + let defaultInput = ''; if (!videoInputs.some((input) => input.deviceId === 'default')) { videoInputs.unshift(defaultOption); } else { From 73f97b46614261e19e92f178b9336c48b99465d4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 2 Jun 2017 01:05:09 +0100 Subject: [PATCH 172/416] bump js-sdk for webrtc --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6076a56d2..33f7314fc6 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "isomorphic-fetch": "^2.2.1", "linkifyjs": "^2.1.3", "lodash": "^4.13.1", - "matrix-js-sdk": "0.7.9", + "matrix-js-sdk": "0.7.10", "optimist": "^0.6.1", "q": "^1.4.1", "react": "^15.4.0", From 3c5d0f82c901db5f01a9267cc6b60a9de2c37d11 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 2 Jun 2017 01:14:13 +0100 Subject: [PATCH 173/416] Prepare changelog for v0.9.0-rc.2 --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23098c4749..7102c43f24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +Changes in [0.9.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.9.0-rc.2) (2017-06-02) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.9.0-rc.1...v0.9.0-rc.2) + + * Update from Weblate. + [\#1002](https://github.com/matrix-org/matrix-react-sdk/pull/1002) + * webrtc config electron + [\#850](https://github.com/matrix-org/matrix-react-sdk/pull/850) + * enable useCompactLayout user setting an add a class when it's enabled + [\#986](https://github.com/matrix-org/matrix-react-sdk/pull/986) + * Update from Weblate. + [\#987](https://github.com/matrix-org/matrix-react-sdk/pull/987) + * Translation fixes for everything but src/components + [\#990](https://github.com/matrix-org/matrix-react-sdk/pull/990) + * Fix tests + [\#1001](https://github.com/matrix-org/matrix-react-sdk/pull/1001) + * Fix tests for PR #989 + [\#999](https://github.com/matrix-org/matrix-react-sdk/pull/999) + * Revert "Revert "add labels to language picker"" + [\#1000](https://github.com/matrix-org/matrix-react-sdk/pull/1000) + * maybe fixxy [Electron] external thing? + [\#997](https://github.com/matrix-org/matrix-react-sdk/pull/997) + * travisci: Don't run the riot-web tests if the react-sdk tests fail + [\#992](https://github.com/matrix-org/matrix-react-sdk/pull/992) + * Support 12hr time on DateSeparator + [\#991](https://github.com/matrix-org/matrix-react-sdk/pull/991) + * Revert "add labels to language picker" + [\#994](https://github.com/matrix-org/matrix-react-sdk/pull/994) + * Call MatrixClient.clearStores on logout + [\#983](https://github.com/matrix-org/matrix-react-sdk/pull/983) + * Matthew/room avatar event + [\#988](https://github.com/matrix-org/matrix-react-sdk/pull/988) + * add labels to language picker + [\#989](https://github.com/matrix-org/matrix-react-sdk/pull/989) + * Update from Weblate. + [\#981](https://github.com/matrix-org/matrix-react-sdk/pull/981) + Changes in [0.9.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.9.0-rc.1) (2017-06-01) ============================================================================================================= [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.9...v0.9.0-rc.1) From 8add074dbfaa54d02e8bef3fcc9d02c327633cc2 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 2 Jun 2017 01:14:13 +0100 Subject: [PATCH 174/416] v0.9.0-rc.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3cb87ce716..1b00a57d52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "0.9.0-rc.1", + "version": "0.9.0-rc.2", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 364e7d6ae9afd0a22dadfc94d3a847c1a8ec921e Mon Sep 17 00:00:00 2001 From: dtygel Date: Fri, 2 Jun 2017 02:54:52 +0000 Subject: [PATCH 175/416] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (797 of 797 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 | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index f987a0cd68..4abfa0da42 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -193,7 +193,7 @@ "sent an invitation to": "enviou um convite para", "sent a video": "enviou um vídeo", "Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado", - "Server may be unavailable, overloaded, or you hit a bug.": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução.", + "Server may be unavailable, overloaded, or you hit a bug.": "O servidor pode estar indisponível ou sobrecarregado, ou então você encontrou uma falha no sistema.", "Session ID": "Identificador de sessão", "set a profile picture": "colocou uma foto de perfil", "set their display name to": "configurou seu nome para", @@ -619,9 +619,9 @@ "No devices with registered encryption keys": "Não há dispositivos com chaves de criptografia registradas", "No more results": "Não há mais resultados", "No results": "Sem resultados", - "OK": "OK", + "OK": "Ok", "Revoke Moderator": "Retirar status de moderador", - "Search": "Localizar", + "Search": "Buscar", "Search failed": "Busca falhou", "Server error": "Erro no servidor", "Server may be unavailable, overloaded, or search timed out :(": "O servidor pode estar indisponível, sobrecarregado, ou a busca ultrapassou o tempo limite :(", @@ -741,7 +741,6 @@ "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.": "Este processo permite que você exporte as chaves para mensagens que você recebeu em salas criptografadas para um arquivo local. Você poderá então importar o arquivo para outro cliente Matrix no futuro, de modo que este cliente também poderá descriptografar suas mensagens.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "O arquivo exportado será protegido com uma senha. Você deverá inserir a senha aqui para poder descriptografar o arquivo futuramente.", "You must join the room to see its files": "Você precisa ingressar na sala para ver seus arquivos", - "Server may be unavailable, overloaded, or you hit a bug.": "O servidor pode estar indisponível ou sobrecarregado, ou então você encontrou uma falha no sistema.", "Reject all %(invitedRooms)s invites": "Rejeitar todos os %(invitedRooms)s convites", "Start new Chat": "Iniciar nova conversa", "Guest users can't invite users. Please register.": "Visitantes não podem convidar usuárias(os) registradas(os). Favor registrar.", @@ -820,5 +819,33 @@ "Start automatically after system login": "Iniciar automaticamente ao iniciar o sistema", "Desktop specific": "Específico para o app de computadores desktop", "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?": "Você será levado agora a um site de terceiros para poder autenticar a sua conta para uso com o serviço %(integrationsUrl)s. Você quer continuar?", - "Disable URL previews for this room (affects only you)": "Desabilitar as pré-visualizações de sites para esta sala (afeta apenas a você)" + "Disable URL previews for this room (affects only you)": "Desabilitar as pré-visualizações de sites para esta sala (afeta apenas a você)", + "Device already verified!": "Dispositivo já verificado!", + "disabled": "desabilitado", + "enabled": "habilitado", + "Export": "Exportar", + "Failed to register as guest:": "Falha ao se registrar como visitante:", + "Guest access is disabled on this Home Server.": "O acesso para visitantes está desabilitado neste Servidor de Base.", + "Import": "Importar", + "Incorrect username and/or password.": "Nome de usuária(o) e/ou senha incorreto.", + "Invited": "Convidada(o)", + "Results from DuckDuckGo": "Resultados de DuckDuckGo", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "A chave de assinatura que você forneceu é a mesma que a chave de assinatura que você recebeu do dispositivo %(deviceId)s de %(userId)s . O dispositivo foi portanto marcado como verificado.", + "This Home Server does not support login using email address.": "Este Servidor de Base não permite login usando endereço de e-mail.", + "There was a problem logging in.": "Houve um problema ao fazer login.", + "Unknown (user, device) pair:": "Par usuária(o)-dispositivo desconhecido:", + "Unrecognised command:": "Comando não reconhecido:", + "Unrecognised room alias:": "Apelido de sala não reconhecido:", + "Use compact timeline layout": "Usar o layout de linha do tempo compacta", + "Verified key": "Chave verificada", + "WARNING: Device already verified, but keys do NOT MATCH!": "ATENÇÃO: O dispositivo já foi verificado, mas as chaves NÃO SÃO IGUAIS!", + "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!": "ATENÇÃO: VERIFICAÇÃO DE CHAVE FALHOU! A chave de assinatura para a(o) usuária(o) %(userId)s e dispositivo %(deviceId)s é \"%(fprint)s\", que não é igual à chave fornecida \"%(fingerprint)s\". Isso pode significar que suas comunicações estão sendo interceptadas!", + "Set a Display Name": "Definir seu nome público", + "for %(amount)ss": "por %(amount)ss", + "for %(amount)sm": "por %(amount)sm", + "for %(amount)sh": "por %(amount)sh", + "for %(amount)sd": "por %(amount)sd", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a imagem da sala.", + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s alterou a imagem da sala %(roomName)s", + "$senderDisplayName changed the room avatar to ": "$senderDisplayName alterou a imagem da sala para " } From a8acffa8e1005f42ec53bf5ec2491103ed734306 Mon Sep 17 00:00:00 2001 From: jx tsai Date: Fri, 2 Jun 2017 02:28:05 +0000 Subject: [PATCH 176/416] Translated using Weblate (Chinese (Traditional)) Currently translated at 38.8% (310 of 797 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 306 +++++++++++++++++++++++++++++++++- 1 file changed, 297 insertions(+), 9 deletions(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 5238e647b1..ac41375451 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -1,24 +1,312 @@ { - "An email has been sent to": "電郵已經發送至", - "A new password must be entered.": "必須輸入新密碼。.", + "An email has been sent to": "一封郵件已經被發送到", + "A new password must be entered.": "一個新的密碼必須被輸入。.", "anyone": "任何人", - "An error has occurred.": "發生了一個錯誤。", + "An error has occurred.": "一個錯誤出現了。", "Anyone who knows the room's link, apart from guests": "任何知道房間連結的人,但訪客除外", "Anyone who knows the room's link, including guests": "任何知道房間連結的人,包括訪客", - "Are you sure?": "您確認嗎?", + "Are you sure?": "你確定嗎?", "Are you sure you want to reject the invitation?": "您確認要謝絕邀請嗎?", "Are you sure you want to upload the following files?": "您確認要上傳以下文件嗎?", "Attachment": "附件", - "Autoplay GIFs and videos": "自動播放GIF和影片", - "%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s。.", + "Autoplay GIFs and videos": "自動播放GIF和視頻", + "%(senderName)s banned %(targetName)s.": "%(senderName)s 封禁了 %(targetName)s.", "Ban": "封禁", - "Banned users": "已被封禁的使用者", + "Banned users": "被封禁的用戶", "Blacklisted": "已列入黑名單", - "Bug Report": "錯誤報告", + "Bug Report": "臭蟲回報", "Call Timeout": "通話超時", "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "無法連結主伺服器 - 請檢查網路狀況並確保您的 主伺服器 SSL 證書 得到信任", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "當瀏覽器網址列里有 HTTPS URL 時,不能使用 HTTP 連結主伺服器。請採用 HTTPS 或者 允許不安全的腳本", "Can't load user settings": "無法載入使用者設定", "Change Password": "變更密碼", - "%(targetName)s left the room.": "%(targetName)s 離開了聊天室。." + "%(targetName)s left the room.": "%(targetName)s 離開了聊天室。.", + "af": "南非荷蘭語", + "ar-ae": "阿拉伯語 (U.A.E.)", + "ar-bh": "阿拉伯語 (巴林)", + "ar-dz": "阿拉伯語 (阿爾吉利亞)", + "ar-eg": "阿拉伯語 (埃及)", + "ar-iq": "阿拉伯語 (伊拉克)", + "ar-jo": "阿拉伯語 (約旦)", + "ar-kw": "阿拉伯語 (科威特)", + "ar-lb": "阿拉伯語 (黎巴嫩)", + "ar-ly": "阿拉伯語 (利比亞)", + "ar-ma": "阿拉伯語 (摩洛哥)", + "ar-om": "阿拉伯語 (阿曼)", + "ar-qa": "阿拉伯語 (卡達)", + "ar-sa": "阿拉伯語 (沙烏地阿拉伯)", + "ar-sy": "阿拉伯語 (敍利亞)", + "ar-tn": "阿拉伯語 (突尼斯)", + "ar-ye": "阿拉伯語 (葉門)", + "be": "白俄羅斯語", + "bg": "保加利亞", + "ca": "加泰羅尼亞語", + "cs": "捷克語", + "da": "丹麥語", + "de-at": "德語(奧地利)", + "de-ch": "德語(瑞士)", + "de": "德語", + "de-lu": "德語(盧森堡)", + "el": "希臘語", + "en-au": "英語(澳大利亞)", + "en-bz": "英語 (貝里茲)", + "en-ca": "英語 (加拿大)", + "en": "英語", + "en-gb": "英語 (英國)", + "en-ie": "英語 (愛爾蘭)", + "en-jm": "英語 (牙買加)", + "en-nz": "英語 (新西蘭)", + "en-tt": "英語 (千里達)", + "en-us": "英語 (美國)", + "en-za": "英語 (南非)", + "es-ar": "西班牙語 (阿根廷)", + "es-bo": "西班牙語 (波利維亞)", + "es-cl": "西班牙語 (智利)", + "es-co": "西班牙語 (哥倫比亞)", + "es-cr": "西班牙語 (哥斯大黎加)", + "es-do": "西班牙語 (多明尼加共和國)", + "es-ec": "西班牙語 (厄瓜多)", + "es-gt": "西班牙語 (瓜地馬拉)", + "es-hn": "西班牙語 (宏都拉斯)", + "es-mx": "西班牙語 (墨西哥)", + "es-ni": "西班牙語 (尼加拉瓜)", + "es-pa": "西班牙語 (巴拿馬)", + "es-pe": "西班牙語 (祕魯)", + "es-pr": "西班牙語 (波多黎各)", + "es-py": "西班牙語 (巴拉圭)", + "es": "西班牙語 (西班牙)", + "es-sv": "西班牙語 (薩爾瓦多)", + "es-uy": "西班牙語 (烏拉圭)", + "es-ve": "西班牙語 (委內瑞拉)", + "fr-be": "法語 (比利時)", + "fr-ca": "法語 (加拿大)", + "fr-ch": "法語 (瑞士)", + "fr": "法語 (法國)", + "fr-lu": "法語 (慮森堡)", + "zh-cn": "中文(中國)", + "zh-hk": "中文(香港)", + "zh-sg": "中文(新加坡)", + "zh-tw": "中文(台灣)", + "zu": "祖魯語", + "accept": "接受", + "Account": "帳號", + "Access Token:": "取用令牌:", + "Add email address": "添加郵件地址", + "Add phone number": "添加電話號碼", + "Admin": "管理者", + "Advanced": "高級", + "Algorithm": "算法", + "Always show message timestamps": "總是顯示消息時間戳", + "Authentication": "授權", + "all room members": "所有聊天室成員", + "all room members, from the point they are invited": "所有聊天室成員,從他們被邀請開始", + "all room members, from the point they joined": "所有聊天室成員,從他們加入開始", + "an address": "一個地址", + "and": "和", + "%(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...": "與另一個...", + "%(names)s and %(lastPerson)s are typing": "%(names)s 和 %(lastPerson)s 正在打字", + "%(names)s and %(count)s others are typing": "%(names)s 和另外 %(count)s 個人正在打字", + "%(senderName)s answered the call.": "%(senderName)s 接了通話。.", + "Clear Cache": "清理緩存", + "Click here": "點擊這里", + "Click here to fix": "點擊這里修復", + "Confirm password": "確認密碼", + "Confirm your new password": "確認你的新密碼", + "Continue": "繼續", + "Create an account": "創建新帳號", + "Create Room": "創建聊天室", + "Cryptography": "加密", + "Current password": "當前密碼", + "/ddg is not a command": "/ddg 不是一個命令", + "Deactivate Account": "銷毀賬號", + "Deactivate my account": "銷毀我的帳號", + "decline": "拒絕", + "Decrypt %(text)s": "解密 %(text)s", + "Decryption error": "解密出錯", + "Delete": "刪除", + "Default": "默認", + "Device ID": "設備識別碼", + "Devices": "設備列表", + "Devices will not yet be able to decrypt history from before they joined the room": "新加入聊天室的設備不能解密加入之前的聊天記錄", + "Direct Chat": "私聊", + "Direct chats": "私聊", + "Disable inline URL previews by default": "默認禁用自動網址預覽", + "Disinvite": "取消邀請", + "Display name": "顯示名稱", + "Displays action": "顯示操作", + "Don't send typing notifications": "不要發送我的打字狀態", + "Download %(text)s": "下載 %(text)s", + "Drop here %(toAction)s": "拖拽到這里 %(toAction)s", + "Ed25519 fingerprint": "Ed25519指紋", + "Email": "電子郵箱", + "Email address": "電子郵箱地址", + "Email, name or matrix ID": "電子郵箱,姓名或者matrix ID", + "Emoji": "Emoji", + "Enable encryption": "啟用加密", + "Encrypted messages will not be visible on clients that do not yet implement encryption": "不支持加密的客戶端將看不到加密的消息", + "Encrypted room": "加密聊天室", + "%(senderName)s ended the call.": "%(senderName)s 結束了通話。.", + "End-to-end encryption information": "端到端加密信息", + "End-to-end encryption is in beta and may not be reliable": "端到端加密現為測試版,不一定可靠", + "Enter Code": "輸入代碼", + "Error": "錯誤", + "Error decrypting attachment": "解密附件時出錯", + "Event information": "事件信息", + "Existing Call": "現有通話", + "Export E2E room keys": "導出聊天室的端到端加密密鑰", + "Failed to ban user": "封禁用戶失敗", + "Failed to change password. Is your password correct?": "修改密碼失敗。確認原密碼輸入正確嗎?", + "Failed to delete device": "刪除設備失敗", + "Failed to forget room %(errCode)s": "無法忘記聊天室 %(errCode)s", + "Failed to join room": "無法加入聊天室", + "Failed to join the room": "無法加入此聊天室", + "Failed to kick": "踢人失敗", + "Failed to leave room": "無法離開聊天室", + "Failed to load timeline position": "無法加載時間軸位置", + "Failed to lookup current room": "找不到當前聊天室", + "Failed to mute user": "禁言用戶失敗", + "Failed to reject invite": "拒絕邀請失敗", + "Failed to reject invitation": "拒絕邀請失敗", + "Failed to save settings": "保存設置失敗", + "Failed to send email": "發送郵件失敗", + "Failed to send request.": "發送請求失敗。", + "Failed to set avatar.": "設置頭像失敗。.", + "Failed to set display name": "設置暱稱失敗", + "Failed to set up conference call": "無法啟動群組通話", + "Failed to toggle moderator status": "無法切換管理員權限", + "Failed to unban": "解除封禁失敗", + "Failed to upload file": "上傳文件失敗", + "Failed to verify email address: make sure you clicked the link in the email": "郵箱驗證失敗: 請確保你已點擊郵件中的鏈接", + "Failure to create room": "創建聊天室失敗", + "Favourite": "收藏", + "favourite": "收藏", + "Favourites": "收藏夾", + "Fill screen": "全螢幕顯示", + "Filter room members": "過濾聊天室成員", + "Forget room": "忘記聊天室", + "Forgot your password?": "忘記密碼?", + "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.": "出於安全考慮,用戶注銷時會清除瀏覽器里的端到端加密密鑰。如果你想要下次登錄 Riot 時能解密過去的聊天記錄,請導出你的聊天室密鑰。", + "Found a bug?": "發現漏洞?", + "%(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": "游客不能上傳文件。請注冊以上傳文件", + "Guests can't use labs features. Please register.": "游客不能使用實驗性功能。請注冊。.", + "Guests cannot join this room even if explicitly invited.": "游客不能加入此聊天室,即使有人主動邀請。.", + "had": "已經", + "Hangup": "掛斷", + "Hide read receipts": "隱藏已讀回執", + "Hide Text Formatting Toolbar": "隱藏格式工具欄", + "Historical": "曆史", + "Homeserver is": "主服務器是", + "Identity Server is": "身份認證服務器是", + "I have verified my email address": "我已經驗證了我的郵箱地址", + "Import E2E room keys": "導入聊天室端對端加密密鑰", + "Incorrect verification code": "驗證碼錯誤", + "Interface Language": "界面語言", + "Invalid alias format": "別名格式錯誤", + "Invalid address format": "地址格式錯誤", + "Invalid Email Address": "郵箱地址格式錯誤", + "Invalid file%(extra)s": "非法文件%(extra)s", + "Invite new room members": "邀請新的聊天室成員", + "Join Room": "加入聊天室", + "joined": "加入了", + "%(targetName)s joined the room.": "%(targetName)s 加入了聊天室。.", + "Jump to first unread message.": "跳到第一條未讀消息。", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s 把 %(targetName)s 踢出了聊天室。.", + "Leave room": "離開聊天室", + "Login as guest": "以游客的身份登錄", + "New password": "新密碼", + "Report it": "報告", + "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "重設密碼會導致所有設備上的端到端加密密鑰被重置,使得加密的聊天記錄不可讀,除非你事先導出密鑰,修改密碼后再導入。此問題將來會得到改善。.", + "restore": "恢復", + "Return to app": "返回 App", + "Return to login screen": "返回登錄頁面", + "Riot does not have permission to send you notifications - please check your browser settings": "Riot 未被允許向你推送消息 - 請檢查瀏覽器設置", + "Riot was not given permission to send notifications - please try again": "Riot 未被允許推送消息通知 - 請重試", + "riot-web version:": "riot-網頁版:", + "Room %(roomId)s not visible": "聊天室 %(roomId)s 已隱藏", + "Room Colour": "聊天室顏色", + "Room name (optional)": "聊天室名稱 (可選)", + "Rooms": "聊天室", + "Scroll to bottom of page": "滾動到頁面底部", + "Scroll to unread messages": "滾動到未讀消息", + "Search": "搜索", + "Search failed": "搜索失敗", + "Searches DuckDuckGo for results": "搜索 DuckDuckGo", + "Send a message (unencrypted)": "發送消息 (非加密)", + "Send an encrypted message": "發送加密消息", + "Sender device information": "發送者的設備信息", + "Send Invites": "發送邀請", + "Send Reset Email": "發送密碼重設郵件", + "sent an image": "發了一張圖片", + "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s 發了一張圖片。.", + "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s 向 %(targetDisplayName)s 發了加入聊天室的邀請。.", + "sent a video": "發了一個視頻", + "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。.", + "Settings": "設置", + "Show panel": "顯示側邊欄", + "Show timestamps in 12 hour format (e.g. 2:30pm)": "用12小時制顯示時間戳 (如:下午 2:30)", + "Signed Out": "已退出登錄", + "Sign in": "登錄", + "Sign out": "注銷", + "since the point in time of selecting this option": "從選擇此選項起", + "since they joined": "從他們加入時起", + "since they were invited": "從他們被邀請時起", + "Some of your messages have not been sent": "部分消息發送失敗", + "Someone": "某個用戶", + "Sorry, this homeserver is using a login which is not recognised ": "很抱歉,無法識別此主伺服器使用的登錄方式 ", + "Start a chat": "創建聊天", + "Start Chat": "開始聊天", + "Submit": "提交", + "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 email address is already in use": "此郵箱地址已經被使用", + "This email address was not found": "未找到此郵箱地址", + "%(actionVerb)s this person?": "%(actionVerb)s 這個用戶?", + "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' 超過了此主伺服器的上傳大小限制", + "The file '%(fileName)s' failed to upload": "文件 '%(fileName)s' 上傳失敗", + "Turn Markdown off": "關閉Markdown 語法", + "Turn Markdown on": "啟用Markdown 語法", + "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s 啟用端對端加密 (algorithm %(algorithm)s).", + "Unable to add email address": "無法加入電郵地址", + "Unable to capture screen": "無法截取畫面", + "Unable to enable Notifications": "無法啟用通知功能", + "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 cannot place a call with yourself.": "你不能打電話給自已", + "You cannot place VoIP calls in this browser.": "在此瀏覽器下無法置入網路電話通話", + "Sun": "星期日", + "Mon": "星期一", + "Tue": "星期二", + "%(severalUsers)sleft": "%(severalUsers)s離開", + "%(oneUser)sleft": "%(oneUser)s離開", + "%(severalUsers)sjoined and left": "%(severalUsers)s加入與離開", + "%(oneUser)sleft and rejoined": "%(oneUser)s離開再重新加入", + "for %(amount)sh": " %(amount)sh", + "for %(amount)sd": " %(amount)sd", + "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 圖像" } From b753563a7e598029adec4c1639a13b09654a3b94 Mon Sep 17 00:00:00 2001 From: Krombel Date: Thu, 1 Jun 2017 21:19:34 +0000 Subject: [PATCH 177/416] Translated using Weblate (German) Currently translated at 97.6% (778 of 797 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 | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 4ed7cef988..d59458a1b7 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 sind in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche sind in diesem Client nicht unterstützt", "Existing Call": "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": "Unfähig Benachrichtigungen zu aktivieren", "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", @@ -608,7 +608,7 @@ "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.", + "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", @@ -830,5 +830,6 @@ "for %(amount)ss": "für %(amount)ss", "for %(amount)sm": "für %(amount)sm", "for %(amount)sh": "für %(amount)sh", - "for %(amount)sd": "für %(amount)sd" + "for %(amount)sd": "für %(amount)sd", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s entfernte Raum-Bild." } From d2d0ba952aba4999fd1f145c6632f45ad9267d06 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 2 Jun 2017 04:23:07 +0100 Subject: [PATCH 178/416] allow hiding redactions. no point onAccountData as it'd cause a full refresh, this may need to be handled differently in the future. Currently handling same as the new timestamp stuff Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MessagePanel.js | 5 +++ src/components/structures/TimelinePanel.js | 52 ++++++++++++---------- src/components/structures/UserSettings.js | 4 ++ src/i18n/strings/en_EN.json | 1 + 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6f49e425fd..5c68d4d206 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -90,6 +90,9 @@ module.exports = React.createClass({ // show timestamps always alwaysShowTimestamps: React.PropTypes.bool, + + // hide redacted events as per old behaviour + hideRedactions: React.PropTypes.bool, }, componentWillMount: function() { @@ -419,6 +422,8 @@ module.exports = React.createClass({ }, _getTilesForEvent: function(prevEvent, mxEv, last) { + if (mxEv.isRedacted() && this.props.hideRedactions) return []; + const EventTile = sdk.getComponent('rooms.EventTile'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); var ret = []; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 76004ebbac..aecb468f71 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -131,6 +131,8 @@ var TimelinePanel = React.createClass({ } } + const syncedSettings = UserSettingsStore.getSyncedSettings(); + return { events: [], timelineLoading: true, // track whether our room timeline is loading @@ -175,10 +177,13 @@ var TimelinePanel = React.createClass({ clientSyncState: MatrixClientPeg.get().getSyncState(), // should the event tiles have twelve hour times - isTwelveHour: UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps'), + isTwelveHour: syncedSettings.showTwelveHourTimestamps, // always show timestamps on event tiles? - alwaysShowTimestamps: UserSettingsStore.getSyncedSetting('alwaysShowTimestamps'), + alwaysShowTimestamps: syncedSettings.alwaysShowTimestamps, + + // hide redacted events as per old behaviour + hideRedactions: syncedSettings.hideRedactions, }; }, @@ -915,7 +920,7 @@ var TimelinePanel = React.createClass({ }); }; } - var message = (error.errcode == 'M_FORBIDDEN') + var message = (error.errcode == 'M_FORBIDDEN') ? _t("Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question") + "." : _t("Tried to load a specific point in this room's timeline, but was unable to find it") + "."; Modal.createDialog(ErrorDialog, { @@ -1113,26 +1118,27 @@ var TimelinePanel = React.createClass({ ); return (