From e22712514eec3c8037a0a77449b1494bccc160f3 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 17 May 2017 10:58:59 +0100 Subject: [PATCH 01/69] Add show / hide apps button --- src/components/views/rooms/MessageComposer.js | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 8a3b128908..90b738f343 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -32,6 +32,8 @@ export default class MessageComposer extends React.Component { this.onCallClick = this.onCallClick.bind(this); this.onHangupClick = this.onHangupClick.bind(this); this.onUploadClick = this.onUploadClick.bind(this); + this.onShowAppsClick = this.onShowAppsClick.bind(this); + this.onHideAppsClick = this.onHideAppsClick.bind(this); this.onUploadFileSelected = this.onUploadFileSelected.bind(this); this.onVoiceCallClick = this.onVoiceCallClick.bind(this); this.onInputContentChanged = this.onInputContentChanged.bind(this); @@ -145,6 +147,7 @@ export default class MessageComposer extends React.Component { } onCallClick(ev) { + console.warn("Call but clicked!"); dis.dispatch({ action: 'place_call', type: ev.shiftKey ? "screensharing" : "video", @@ -160,6 +163,22 @@ export default class MessageComposer extends React.Component { }); } + onShowAppsClick(ev) { + console.warn("Showing apps"); + dis.dispatch({ + action: 'showApps', + room_id: this.props.room.roomId, + }); + } + + onHideAppsClick(ev) { + dis.dispatch({ + action: 'hideApps', + room_id: this.props.room.roomId, + }); + console.warn("Hiding apps"); + } + onInputContentChanged(content: string, selection: {start: number, end: number}) { this.setState({ autocompleteQuery: content, @@ -241,14 +260,13 @@ export default class MessageComposer extends React.Component { alt={e2eTitle} title={e2eTitle} /> ); - var callButton, videoCallButton, hangupButton; + var callButton, videoCallButton, hangupButton, showAppsButton, hideAppsButton; if (this.props.callState && this.props.callState !== 'ended') { hangupButton =
Hangup
; - } - else { + } else { callButton =
@@ -259,6 +277,19 @@ export default class MessageComposer extends React.Component {
; } + // Apps + if (this.props.showAppsState && this.props.showAppsState == 'visible') { + hideAppsButton = +
+ +
; + } else { + showAppsButton = +
+ +
; + } + var canSendMessages = this.props.room.currentState.maySendMessage( MatrixClientPeg.get().credentials.userId); @@ -308,7 +339,9 @@ export default class MessageComposer extends React.Component { uploadButton, hangupButton, callButton, - videoCallButton + videoCallButton, + showAppsButton, + hideAppsButton, ); } else { controls.push( From 9dd0b9bdd1eb648207fa78fadd2f5d84eec0da68 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 17 May 2017 11:31:01 +0100 Subject: [PATCH 02/69] Fix lint errrors / warnings --- src/components/views/rooms/MessageComposer.js | 101 +++++++++--------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 90b738f343..7aab5f1d50 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -13,14 +13,14 @@ 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. */ -var React = require('react'); +const React = require('react'); -var CallHandler = require('../../../CallHandler'); -var MatrixClientPeg = require('../../../MatrixClientPeg'); -var Modal = require('../../../Modal'); -var sdk = require('../../../index'); -var dis = require('../../../dispatcher'); -import Autocomplete from './Autocomplete'; +const CallHandler = require('../../../CallHandler'); +const MatrixClientPeg = require('../../../MatrixClientPeg'); +const Modal = require('../../../Modal'); +const sdk = require('../../../index'); +const dis = require('../../../dispatcher'); +// import Autocomplete from './Autocomplete'; import classNames from 'classnames'; import UserSettingsStore from '../../../UserSettingsStore'; @@ -57,7 +57,6 @@ export default class MessageComposer extends React.Component { }, showFormatting: UserSettingsStore.getSyncedSetting('MessageComposer.showFormatting', false), }; - } componentDidMount() { @@ -82,7 +81,7 @@ export default class MessageComposer extends React.Component { onUploadClick(ev) { if (MatrixClientPeg.get().isGuest()) { - let NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog"); + const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog"); Modal.createDialog(NeedToRegisterDialog, { title: "Please Register", description: "Guest users can't upload files. Please register to upload.", @@ -94,13 +93,14 @@ export default class MessageComposer extends React.Component { } onUploadFileSelected(files, isPasted) { - if (!isPasted) + if (!isPasted) { files = files.target.files; + } - let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - let TintableSvg = sdk.getComponent("elements.TintableSvg"); + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); - let fileList = []; + const fileList = []; for (let i=0; i {files[i].name || 'Attachment'} @@ -121,7 +121,7 @@ export default class MessageComposer extends React.Component { if(shouldUpload) { // MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file if (files) { - for(var i=0; i - + , ); let e2eImg, e2eTitle, e2eClass; @@ -258,9 +260,9 @@ export default class MessageComposer extends React.Component { controls.push( {e2eTitle} + />, ); - var callButton, videoCallButton, hangupButton, showAppsButton, hideAppsButton; + let callButton, videoCallButton, hangupButton, showAppsButton, hideAppsButton; if (this.props.callState && this.props.callState !== 'ended') { hangupButton =
@@ -290,14 +292,14 @@ export default class MessageComposer extends React.Component {
; } - var canSendMessages = this.props.room.currentState.maySendMessage( + const canSendMessages = this.props.room.currentState.maySendMessage( MatrixClientPeg.get().credentials.userId); if (canSendMessages) { // This also currently includes the call buttons. Really we should // check separately for whether we can call, but this is slightly // complex because of conference calls. - var uploadButton = ( + const uploadButton = (
@@ -323,7 +325,7 @@ export default class MessageComposer extends React.Component { controls.push( this.messageComposerInput = c} + ref={(c) => this.messageComposerInput = c} key="controls_input" onResize={this.props.onResize} room={this.props.room} @@ -347,25 +349,25 @@ export default class MessageComposer extends React.Component { controls.push(
You do not have permission to post to this room -
+
, ); } - let autoComplete; - if (UserSettingsStore.isFeatureEnabled('rich_text_editor')) { - autoComplete =
- -
; - } + // let autoComplete; + // if (UserSettingsStore.isFeatureEnabled('rich_text_editor')) { + // autoComplete =
+ // + //
; + // } const {style, blockType} = this.state.inputState; const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map( - name => { + (name) => { const active = style.includes(name) || blockType === name; const suffix = active ? '-o-n' : ''; const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name); @@ -428,5 +430,8 @@ MessageComposer.propTypes = { uploadFile: React.PropTypes.func.isRequired, // opacity for dynamic UI fading effects - opacity: React.PropTypes.number + opacity: React.PropTypes.number, + + // string representing the current room app drawer state + showAppsState: React.PropTypes.string, }; From 95988bd5ecbabc58c3ed80f1d35b17255ec0a9e6 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 17 May 2017 12:35:25 +0100 Subject: [PATCH 03/69] Dispatch show hide app drawer events --- src/components/views/rooms/MessageComposer.js | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 7aab5f1d50..22b84b73cb 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -164,21 +164,19 @@ export default class MessageComposer extends React.Component { } onShowAppsClick(ev) { - alert("Showing apps"); console.warn("Showing apps"); - // dis.dispatch({ - // action: 'showApps', - // room_id: this.props.room.roomId, - // }); + dis.dispatch({ + action: 'showApps', + room_id: this.props.room.roomId, + }); } onHideAppsClick(ev) { - alert("Hiding apps"); console.warn("Hiding apps"); - // dis.dispatch({ - // action: 'hideApps', - // room_id: this.props.room.roomId, - // }); + dis.dispatch({ + action: 'hideApps', + room_id: this.props.room.roomId, + }); } onInputContentChanged(content: string, selection: {start: number, end: number}) { @@ -282,12 +280,12 @@ export default class MessageComposer extends React.Component { // Apps if (this.props.showAppsState && this.props.showAppsState == 'visible') { hideAppsButton = -
+
; } else { showAppsButton = -
+
; } From 7e1de2ac350e7f2ea5eab9eb11e1f7caf2db63e2 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 17 May 2017 21:15:57 +0100 Subject: [PATCH 04/69] Show/hide apps panel and misc formatting and lint fixes --- src/components/structures/RoomView.js | 25 ++++++++++---- src/components/views/rooms/AuxPanel.js | 34 +++++++++++-------- src/components/views/rooms/MessageComposer.js | 14 ++++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index b22d867acf..49aa3a0af5 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -43,7 +43,7 @@ import KeyCode from '../../KeyCode'; import UserProvider from '../../autocomplete/UserProvider'; -var DEBUG = false; +const DEBUG = false; if (DEBUG) { // using bind means that we get to keep useful line numbers in the console @@ -133,6 +133,7 @@ module.exports = React.createClass({ callState: null, guestsCanJoin: false, canPeek: false, + showApps: false, // error object, as from the matrix client/server API // If we failed to load information about the room, @@ -168,7 +169,7 @@ module.exports = React.createClass({ onClickCompletes: true, onStateChange: (isCompleting) => { this.forceUpdate(); - } + }, }); if (this.props.roomAddress[0] == '#') { @@ -434,9 +435,14 @@ module.exports = React.createClass({ this._updateConfCallNotification(); this.setState({ - callState: callState + callState: callState, }); + break; + case 'appsDrawer': + this.setState({ + showApps: payload.show ? true : false, + }); break; } }, @@ -1638,7 +1644,8 @@ module.exports = React.createClass({ draggingFile={this.state.draggingFile} displayConfCallNotification={this.state.displayConfCallNotification} maxHeight={this.state.auxPanelMaxHeight} - onResize={this.onChildResize} > + onResize={this.onChildResize} + showApps={this.state.showApps} > { aux } ); @@ -1651,8 +1658,14 @@ module.exports = React.createClass({ if (canSpeak) { messageComposer = ; + room={this.state.room} + onResize={this.onChildResize} + uploadFile={this.uploadFile} + callState={this.state.callState} + tabComplete={this.tabComplete} + opacity={ this.props.opacity } + showApps={ this.state.showApps } + />; } // TODO: Why aren't we storing the term/scope/count in this format diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index 365cc18f99..31739a890f 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -var React = require('react'); -var MatrixClientPeg = require("../../../MatrixClientPeg"); -var sdk = require('../../../index'); -var dis = require("../../../dispatcher"); -var ObjectUtils = require('../../../ObjectUtils'); +const React = require('react'); +const MatrixClientPeg = require("../../../MatrixClientPeg"); +const sdk = require('../../../index'); +const dis = require("../../../dispatcher"); +const ObjectUtils = require('../../../ObjectUtils'); +const AppsDrawer = require('./AppsDrawer'); module.exports = React.createClass({ displayName: 'AuxPanel', @@ -68,10 +69,10 @@ module.exports = React.createClass({ }, render: function() { - var CallView = sdk.getComponent("voip.CallView"); - var TintableSvg = sdk.getComponent("elements.TintableSvg"); + const CallView = sdk.getComponent("voip.CallView"); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); - var fileDropTarget = null; + let fileDropTarget = null; if (this.props.draggingFile) { fileDropTarget = (
@@ -85,19 +86,18 @@ module.exports = React.createClass({ ); } - var conferenceCallNotification = null; + let conferenceCallNotification = null; if (this.props.displayConfCallNotification) { - var supportedText, joinText; + let supportedText; + let joinText; if (!MatrixClientPeg.get().supportsVoip()) { supportedText = " (unsupported)"; - } - else { + } else { joinText = ( Join as { this.onConferenceNotificationClick(event, 'voice');}} href="#">voice or { this.onConferenceNotificationClick(event, 'video'); }} href="#">video. ); - } conferenceCallNotification = (
@@ -106,7 +106,7 @@ module.exports = React.createClass({ ); } - var callView = ( + const callView = ( ); + let appsDrawer = null; + if(this.props.showApps) { + appsDrawer = ; + } + return (
+ { appsDrawer } { fileDropTarget } { callView } { conferenceCallNotification } diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 22b84b73cb..a90de8cb5c 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -164,18 +164,16 @@ export default class MessageComposer extends React.Component { } onShowAppsClick(ev) { - console.warn("Showing apps"); dis.dispatch({ - action: 'showApps', - room_id: this.props.room.roomId, + action: 'appsDrawer', + show: true, }); } onHideAppsClick(ev) { - console.warn("Hiding apps"); dis.dispatch({ - action: 'hideApps', - room_id: this.props.room.roomId, + action: 'appsDrawer', + show: false, }); } @@ -278,7 +276,7 @@ export default class MessageComposer extends React.Component { } // Apps - if (this.props.showAppsState && this.props.showAppsState == 'visible') { + if (this.props.showApps) { hideAppsButton =
@@ -431,5 +429,5 @@ MessageComposer.propTypes = { opacity: React.PropTypes.number, // string representing the current room app drawer state - showAppsState: React.PropTypes.string, + showApps: React.PropTypes.bool, }; From 0e5657333fe17df739f5736efd834ea0e7f926d5 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 17 May 2017 23:21:02 +0100 Subject: [PATCH 05/69] Add app drawer and app dialog --- src/components/views/dialogs/AddAppDialog.js | 74 ++++++++++++++++++++ src/components/views/rooms/AppsDrawer.js | 52 ++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/components/views/dialogs/AddAppDialog.js create mode 100644 src/components/views/rooms/AppsDrawer.js diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js new file mode 100644 index 0000000000..4a3c1dfb5b --- /dev/null +++ b/src/components/views/dialogs/AddAppDialog.js @@ -0,0 +1,74 @@ +/* +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'; + +/** + * Prompt the user for address of iframe widget + * + * On success, `onFinished(true, newAppWidget)` is called. + */ +export default React.createClass({ + displayName: 'AddAppDialog', + propTypes: { + onFinished: React.PropTypes.func.isRequired, + }, + + getInitialState: function() { + }, + + 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 ( + +
+ Please enter the URL of the app / widget to add. +
+
+
+ +
+
+ +
+
+
+ ); + }, +}); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js new file mode 100644 index 0000000000..82abb07e5c --- /dev/null +++ b/src/components/views/rooms/AppsDrawer.js @@ -0,0 +1,52 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +const React = require('react'); +const AddAppDialog = require('../dialogs/AddAppDialog'); + +module.exports = React.createClass({ + displayName: 'AppsDrawer', + + propTypes: { + }, + + componentDidMount: function() { + }, + + getInitialState: function() { + // this.onClickAddWidget = this.onClickAddWidget.bind(this); + return { + addAppWidget: false, + }; + }, + + onClickAddWidget: function() { + this.setState({ + addAppWidget: true, + }); + }, + + render: function() { + return ( +
+
[+] Add a widget
+ {this.state.addAppWidget && AddAppDialog} +
+ ); + }, +}); From e8837d28ef7b8a37756b16e5c411a5ff7c3becf6 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 22 May 2017 12:34:27 +0100 Subject: [PATCH 06/69] App tile and app dialog styling --- src/components/views/dialogs/AddAppDialog.js | 9 +- src/components/views/elements/AppTile.js | 53 +++++++++++ src/components/views/elements/MemberTile.js | 97 ++++++++++++++++++++ src/components/views/rooms/AppsDrawer.js | 39 ++++++-- 4 files changed, 188 insertions(+), 10 deletions(-) create mode 100644 src/components/views/elements/AppTile.js create mode 100644 src/components/views/elements/MemberTile.js diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index 4a3c1dfb5b..b72a3809b4 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -29,6 +29,9 @@ export default React.createClass({ }, getInitialState: function() { + return { + value: "", + }; }, componentDidMount: function() { @@ -36,9 +39,7 @@ export default React.createClass({ }, onValueChange: function(ev) { - this.setState({ - value: ev.target.value, - }); + this.setState({ value: ev.target.value}); }, onFormSubmit: function(ev) { @@ -61,7 +62,7 @@ export default React.createClass({
diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js new file mode 100644 index 0000000000..66c32a16a1 --- /dev/null +++ b/src/components/views/elements/AppTile.js @@ -0,0 +1,53 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +const React = require('react'); + +export default React.createClass({ + displayName: 'AppTile', + + propTypes: { + id: React.PropTypes.string.isRequired, + url: React.PropTypes.string.isRequired, + name: React.PropTypes.string.isRequired, + }, + + getDefaultProps: function() { + return { + url: "", + }; + }, + + render: function() { + return ( +
+
+ {this.props.name} + + Edit + Cancel + {/* x */} + +
+
+ +
+
+ ); + }, +}); diff --git a/src/components/views/elements/MemberTile.js b/src/components/views/elements/MemberTile.js new file mode 100644 index 0000000000..5becef9ede --- /dev/null +++ b/src/components/views/elements/MemberTile.js @@ -0,0 +1,97 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); + +var MatrixClientPeg = require('../../../MatrixClientPeg'); +var sdk = require('../../../index'); +var dis = require('../../../dispatcher'); +var Modal = require("../../../Modal"); + +module.exports = React.createClass({ + displayName: 'MemberTile', + + propTypes: { + member: React.PropTypes.any.isRequired, // RoomMember + }, + + getInitialState: function() { + return {}; + }, + + shouldComponentUpdate: function(nextProps, nextState) { + if ( + this.member_last_modified_time === undefined || + this.member_last_modified_time < nextProps.member.getLastModifiedTime() + ) { + return true; + } + if ( + nextProps.member.user && + (this.user_last_modified_time === undefined || + this.user_last_modified_time < nextProps.member.user.getLastModifiedTime()) + ) { + return true; + } + return false; + }, + + onClick: function(e) { + dis.dispatch({ + action: 'view_user', + member: this.props.member, + }); + }, + + _getDisplayName: function() { + return this.props.member.name; + }, + + getPowerLabel: function() { + return this.props.member.userId + " (power " + this.props.member.powerLevel + ")"; + }, + + render: function() { + var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + var EntityTile = sdk.getComponent('rooms.EntityTile'); + + var member = this.props.member; + var name = this._getDisplayName(); + var active = -1; + var presenceState = member.user ? member.user.presence : null; + + var av = ( + + ); + + if (member.user) { + this.user_last_modified_time = member.user.getLastModifiedTime(); + } + this.member_last_modified_time = member.getLastModifiedTime(); + + return ( + + ); + } +}); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 82abb07e5c..ef0cbcf2b9 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -18,6 +18,8 @@ limitations under the License. const React = require('react'); const AddAppDialog = require('../dialogs/AddAppDialog'); +const AppTile = require('../elements/AppTile'); +const Modal = require("../../../Modal"); module.exports = React.createClass({ displayName: 'AppsDrawer', @@ -26,26 +28,51 @@ module.exports = React.createClass({ }, componentDidMount: function() { + const as = this.state.apps; + as.push({ + id: "bbcApp", + url: "http://news.bbc.co.uk", + name: "BBC News", + }); + this.setState({apps: as}); }, getInitialState: function() { - // this.onClickAddWidget = this.onClickAddWidget.bind(this); return { - addAppWidget: false, + apps: [{ + id: "googleApp", + url: "http://matrix.org/grafana/dashboard/db/golang-metrics?panelId=2&fullscreen&edit&var-bucket_size=1m&var-job=riot-bot&var-handler=All&from=1495188444653&to=1495210044654", + name: "Google", + }], }; }, onClickAddWidget: function() { - this.setState({ - addAppWidget: true, + Modal.createDialog(AddAppDialog, { + onFinished: (proceed, reason) => { + if (!proceed) return; + + this.state.apps.push(); + }, }); }, render: function() { + const apps = this.state.apps.map( + (app) => ); + return (
-
[+] Add a widget
- {this.state.addAppWidget && AddAppDialog} +
+ {apps} +
+
+ [+] Add a widget +
); }, From b111579aed578dae1089c7c349a72c2a0977c551 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 22 May 2017 18:00:17 +0100 Subject: [PATCH 07/69] App tile events --- src/components/views/elements/AppTile.js | 27 ++++++++++++++++++++---- src/components/views/rooms/AppsDrawer.js | 6 +++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 66c32a16a1..72d59d0ecd 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -33,19 +33,38 @@ export default React.createClass({ }; }, + _onEditClick: function() { + console.log("Edit widget %s", this.props.id); + }, + + _onDeleteClick: function() { + console.log("Delete widget %s", this.props.id); + }, + render: function() { return (
{this.props.name} - Edit - Cancel - {/* x */} + {/* Edit widget */} + Edit + + {/* Delete widget */} + Cancel
- +
); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index ef0cbcf2b9..1318d07d8b 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -40,9 +40,9 @@ module.exports = React.createClass({ getInitialState: function() { return { apps: [{ - id: "googleApp", - url: "http://matrix.org/grafana/dashboard/db/golang-metrics?panelId=2&fullscreen&edit&var-bucket_size=1m&var-job=riot-bot&var-handler=All&from=1495188444653&to=1495210044654", - name: "Google", + id: "riot-bot", + url: "https://matrix.org/_matrix/media/v1/thumbnail/matrix.org/LvHiqFMHWxAjFUMVCvaPbRYs?width=150&height=150", + name: "Riot-bot", }], }; }, From ec03cf4de39ec9a815af6a8b2d6d098c712146b4 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 30 May 2017 10:46:51 +0100 Subject: [PATCH 08/69] disable iframe sandboxing. Remove BBC news iframe --- src/components/views/elements/AppTile.js | 3 +- src/components/views/rooms/AppsDrawer.js | 36 ++++++++++++++++-------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 72d59d0ecd..18b2148829 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -64,7 +64,8 @@ export default React.createClass({
- + {/* */} +
); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 1318d07d8b..041e0f0943 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -28,22 +28,34 @@ module.exports = React.createClass({ }, componentDidMount: function() { - const as = this.state.apps; - as.push({ - id: "bbcApp", - url: "http://news.bbc.co.uk", - name: "BBC News", - }); - this.setState({apps: as}); + // const as = this.state.apps; + // as.push({ + // id: "bbcApp", + // url: "http://news.bbc.co.uk", + // name: "BBC News", + // }); + // this.setState({apps: as}); }, getInitialState: function() { return { - apps: [{ - id: "riot-bot", - url: "https://matrix.org/_matrix/media/v1/thumbnail/matrix.org/LvHiqFMHWxAjFUMVCvaPbRYs?width=150&height=150", - name: "Riot-bot", - }], + apps: [ + // { + // id: "riot-bot", + // url: "https://matrix.org/_matrix/media/v1/thumbnail/matrix.org/LvHiqFMHWxAjFUMVCvaPbRYs?width=150&height=150", + // name: "Riot-bot", + // }, + { + id: "youtube", + url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1", + name: "Live stream - Boeuf Bourguignon", + }, + { + id: "recipie", + url: "https://www.bbcgoodfood.com/recipes/5032/beef-bourguignon", + name: "Ingredients - Boeuf Bourguignon", + }, + ], }; }, From 143f68ec56242b7eea84a9e3255bd5eb1b9c6af8 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 30 May 2017 11:40:29 +0100 Subject: [PATCH 09/69] Add locally hosted recepie widget --- src/components/views/rooms/AppsDrawer.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 041e0f0943..3e71d46feb 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -28,23 +28,11 @@ module.exports = React.createClass({ }, componentDidMount: function() { - // const as = this.state.apps; - // as.push({ - // id: "bbcApp", - // url: "http://news.bbc.co.uk", - // name: "BBC News", - // }); - // this.setState({apps: as}); }, getInitialState: function() { return { apps: [ - // { - // id: "riot-bot", - // url: "https://matrix.org/_matrix/media/v1/thumbnail/matrix.org/LvHiqFMHWxAjFUMVCvaPbRYs?width=150&height=150", - // name: "Riot-bot", - // }, { id: "youtube", url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1", @@ -52,7 +40,7 @@ module.exports = React.createClass({ }, { id: "recipie", - url: "https://www.bbcgoodfood.com/recipes/5032/beef-bourguignon", + url: "http://localhost:8000/recepie.html", name: "Ingredients - Boeuf Bourguignon", }, ], From 0e7bb6791f5448cbd93efbc8bdeebfc2a92260d4 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 30 May 2017 13:47:17 +0100 Subject: [PATCH 10/69] Static widget config per room --- src/components/views/rooms/AppsDrawer.js | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 3e71d46feb..a571cc8047 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -21,6 +21,22 @@ const AddAppDialog = require('../dialogs/AddAppDialog'); const AppTile = require('../elements/AppTile'); const Modal = require("../../../Modal"); +// FIXME -- Hard coded widget config +const roomWidgetConfig = { + '!IAkkwswSrOSzPRWksX:matrix.org': [ + { + id: "youtube", + url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1", + name: "Live stream - Boeuf Bourguignon", + }, + { + id: "recipie", + url: "http://localhost:8000/recepie.html", + name: "Ingredients - Boeuf Bourguignon", + }, + ], +}; + module.exports = React.createClass({ displayName: 'AppsDrawer', @@ -31,19 +47,15 @@ module.exports = React.createClass({ }, getInitialState: function() { + for (const key in roomWidgetConfig) { + if(key == this.props.room.roomId) { + return { + apps: roomWidgetConfig[key], + }; + } + } return { - apps: [ - { - id: "youtube", - url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1", - name: "Live stream - Boeuf Bourguignon", - }, - { - id: "recipie", - url: "http://localhost:8000/recepie.html", - name: "Ingredients - Boeuf Bourguignon", - }, - ], + apps: [], }; }, From 0d7e3a15f7f2600507ed89a568777dad4779edd3 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 30 May 2017 15:32:53 +0100 Subject: [PATCH 11/69] Add room config --- src/components/views/rooms/AppsDrawer.js | 34 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index a571cc8047..86c20544d9 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -23,10 +23,11 @@ const Modal = require("../../../Modal"); // FIXME -- Hard coded widget config const roomWidgetConfig = { + // Cooking room '!IAkkwswSrOSzPRWksX:matrix.org': [ { id: "youtube", - url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1", + url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", name: "Live stream - Boeuf Bourguignon", }, { @@ -35,6 +36,35 @@ const roomWidgetConfig = { name: "Ingredients - Boeuf Bourguignon", }, ], + // Grafana room + '!JWeMRscvtWqfNuzmSf:matrix.org': [ + { + id: "grafana", + url: "http://localhost:8000/grafana.html", + name: "Monitoring our Single-Point-Of-Failure DB", + }, + { + id: "recipie", + url: "http://localhost:8000/recepie.html", + name: "Ingredients - Boeuf Bourguignon", + }, + ], + // Chat room - https://www.youtube.com/watch?v=ZfkwW4GgAiU + '!wQqrqwOipOOWALxJNe:matrix.org': [ + { + id: "youtube", + url: "https://www.youtube.com/embed/ZfkwW4GgAiU?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", + name: "Live stream - ChatGirl86", + }, + ], + // Game room - https://www.youtube.com/watch?v=Dm2Ma1dOFO4 + '!dYSCwtVljhTdBlgNxq:matrix.org': [ + { + id: "youtube", + url: "https://www.youtube.com/embed/Dm2Ma1dOFO4?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", + name: "Live stream - Overwatch Balle Royale", + }, + ], }; module.exports = React.createClass({ @@ -71,7 +101,7 @@ module.exports = React.createClass({ render: function() { const apps = this.state.apps.map( - (app) => ); + (app) => ); return (
From ae1753bce6710cdf2e70f27c597d1c66f4d8cb44 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 30 May 2017 18:39:51 +0100 Subject: [PATCH 12/69] Add tipping widgets --- src/components/views/rooms/AppsDrawer.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 86c20544d9..7fcf26e69d 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -43,11 +43,6 @@ const roomWidgetConfig = { url: "http://localhost:8000/grafana.html", name: "Monitoring our Single-Point-Of-Failure DB", }, - { - id: "recipie", - url: "http://localhost:8000/recepie.html", - name: "Ingredients - Boeuf Bourguignon", - }, ], // Chat room - https://www.youtube.com/watch?v=ZfkwW4GgAiU '!wQqrqwOipOOWALxJNe:matrix.org': [ @@ -56,6 +51,11 @@ const roomWidgetConfig = { url: "https://www.youtube.com/embed/ZfkwW4GgAiU?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", name: "Live stream - ChatGirl86", }, + { + id: "thermometer", + url: "http://localhost:8000/index.html", + name: "Tip Me!!! -- Send me cash $$$", + }, ], // Game room - https://www.youtube.com/watch?v=Dm2Ma1dOFO4 '!dYSCwtVljhTdBlgNxq:matrix.org': [ @@ -64,6 +64,11 @@ const roomWidgetConfig = { url: "https://www.youtube.com/embed/Dm2Ma1dOFO4?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", name: "Live stream - Overwatch Balle Royale", }, + { + id: "thermometer", + url: "http://localhost:8000/index.html", + name: "Tip Me!!! -- Send me cash $$$", + }, ], }; From dac154f8280bc4db13f8494288ebea0e75ea5bbd Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 31 May 2017 10:08:39 +0100 Subject: [PATCH 13/69] Add full width widgets --- src/components/views/elements/AppTile.js | 2 +- src/components/views/rooms/AppsDrawer.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 18b2148829..338c03ceeb 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -43,7 +43,7 @@ export default React.createClass({ render: function() { return ( -
+
{this.props.name} diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 7fcf26e69d..f729c2032d 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -106,7 +106,13 @@ module.exports = React.createClass({ render: function() { const apps = this.state.apps.map( - (app) => ); + (app, index, arr) => ); return (
From c6991fd33c7c99818a4784f0cf52634d22371867 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Sun, 4 Jun 2017 23:00:52 +0100 Subject: [PATCH 14/69] Periodically pass messages to embedded iframe --- src/components/views/elements/AppTile.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 338c03ceeb..134db04926 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -33,6 +33,16 @@ export default React.createClass({ }; }, + componentDidMount: function() { + console.log("App component %s mounted", this.props.id); + setInterval(() => { + const msg = "Message from riot"; + const domain = 'http://localhost:8000'; + this.refs.appFrame.contentWindow.postMessage(msg, domain); + console.log("Sending message"); + }, 3000); + }, + _onEditClick: function() { console.log("Edit widget %s", this.props.id); }, @@ -65,7 +75,7 @@ export default React.createClass({
{/* */} - +
); From dc4f321707da8d7431c857ab6abf8240e671fb31 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 5 Jun 2017 18:21:31 +0100 Subject: [PATCH 15/69] Pass room and user id to apps draw --- src/components/structures/RoomView.js | 4 ++++ src/components/views/elements/AppTile.js | 3 +-- src/components/views/rooms/AppsDrawer.js | 30 ++++++++++++++++++++++-- src/components/views/rooms/AuxPanel.js | 5 +++- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 49aa3a0af5..af5429fe4e 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -122,6 +122,7 @@ module.exports = React.createClass({ return { room: null, roomId: null, + userId: null, roomLoading: true, editingRoomSettings: false, uploadingRoomSettings: false, @@ -185,6 +186,7 @@ module.exports = React.createClass({ this.setState({ room: room, roomId: result.room_id, + userId: MatrixClientPeg.get().credentials.userId, roomLoading: !room, unsentMessageError: this._getUnsentMessageError(room), }, this._onHaveRoom); @@ -198,6 +200,7 @@ module.exports = React.createClass({ var room = MatrixClientPeg.get().getRoom(this.props.roomAddress); this.setState({ roomId: this.props.roomAddress, + userId: MatrixClientPeg.get().credentials.userId, room: room, roomLoading: !room, unsentMessageError: this._getUnsentMessageError(room), @@ -1640,6 +1643,7 @@ module.exports = React.createClass({ var auxPanel = (
- {/* */} - +
); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index f729c2032d..ed21fb6d77 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -44,7 +44,7 @@ const roomWidgetConfig = { name: "Monitoring our Single-Point-Of-Failure DB", }, ], - // Chat room - https://www.youtube.com/watch?v=ZfkwW4GgAiU + // Camgirl room - https://www.youtube.com/watch?v=ZfkwW4GgAiU '!wQqrqwOipOOWALxJNe:matrix.org': [ { id: "youtube", @@ -70,6 +70,14 @@ const roomWidgetConfig = { name: "Tip Me!!! -- Send me cash $$$", }, ], + // Game room - !BLQjREzUgbtIsgrvRn:matrix.org + '!BLQjREzUgbtIsgrvRn:matrix.org': [ + { + id: "etherpad", + url: "http://localhost:8000/etherpad.html", + name: "Etherpad", + }, + ], }; module.exports = React.createClass({ @@ -81,11 +89,27 @@ module.exports = React.createClass({ componentDidMount: function() { }, + initAppConfig: function(appConfig) { + console.log("App props: ", this.props); + appConfig = appConfig.map( + (app, index, arr) => { + switch(app.id) { + case 'etherpad': + app.url = app.url + '?userName=' + this.props.userId + + '&padId=' + this.props.room.roomId; + break; + } + + return app; + }); + return appConfig; + }, + getInitialState: function() { for (const key in roomWidgetConfig) { if(key == this.props.room.roomId) { return { - apps: roomWidgetConfig[key], + apps: this.initAppConfig(roomWidgetConfig[key]), }; } } @@ -112,6 +136,8 @@ module.exports = React.createClass({ url={app.url} name={app.name} fullWdith={arr.length<2 ? true : false} + roomId={this.props.roomId} + userId={this.props.userId} />); return ( diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index 31739a890f..ec2de2558c 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -116,7 +116,10 @@ module.exports = React.createClass({ let appsDrawer = null; if(this.props.showApps) { - appsDrawer = ; + appsDrawer = ; } return ( From e9f110a4c53e0b0a2a85033700afb2e4d73f27de Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 6 Jun 2017 14:50:43 +0100 Subject: [PATCH 16/69] Don't show add widget if there are more than one existing widgets --- src/components/views/rooms/AppsDrawer.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index ed21fb6d77..37cddf8a2e 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -140,18 +140,21 @@ module.exports = React.createClass({ userId={this.props.userId} />); + const addWidget = this.state.apps && this.state.apps.length < 2 && + (
+ [+] Add a widget +
); + return (
{apps}
-
- [+] Add a widget -
+ {addWidget}
); }, From e8353edb064b2b3d2509ab99826e542763cdf973 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 6 Jun 2017 15:57:40 +0100 Subject: [PATCH 17/69] Disable test postmessag --- src/components/views/elements/AppTile.js | 12 ++++++------ src/components/views/rooms/AppsDrawer.js | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index fd59735014..a3713d2b96 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -35,12 +35,12 @@ export default React.createClass({ componentDidMount: function() { console.log("App component %s mounted", this.props.id); - setInterval(() => { - const msg = "Message from riot"; - const domain = 'http://localhost:8000'; - this.refs.appFrame.contentWindow.postMessage(msg, domain); - console.log("Sending message"); - }, 3000); + // setInterval(() => { + // const msg = "Message from riot"; + // const domain = 'http://localhost:8000'; + // this.refs.appFrame.contentWindow.postMessage(msg, domain); + // console.log("Sending message"); + // }, 3000); }, _onEditClick: function() { diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 37cddf8a2e..af262a9470 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -32,7 +32,7 @@ const roomWidgetConfig = { }, { id: "recipie", - url: "http://localhost:8000/recepie.html", + url: "http://10.9.64.88:8000/recepie.html", name: "Ingredients - Boeuf Bourguignon", }, ], @@ -40,7 +40,7 @@ const roomWidgetConfig = { '!JWeMRscvtWqfNuzmSf:matrix.org': [ { id: "grafana", - url: "http://localhost:8000/grafana.html", + url: "http://10.9.64.88:8000/grafana.html", name: "Monitoring our Single-Point-Of-Failure DB", }, ], @@ -53,7 +53,7 @@ const roomWidgetConfig = { }, { id: "thermometer", - url: "http://localhost:8000/index.html", + url: "http://10.9.64.88:8000/index.html", name: "Tip Me!!! -- Send me cash $$$", }, ], @@ -66,7 +66,7 @@ const roomWidgetConfig = { }, { id: "thermometer", - url: "http://localhost:8000/index.html", + url: "http://10.9.64.88:8000/index.html", name: "Tip Me!!! -- Send me cash $$$", }, ], @@ -74,7 +74,7 @@ const roomWidgetConfig = { '!BLQjREzUgbtIsgrvRn:matrix.org': [ { id: "etherpad", - url: "http://localhost:8000/etherpad.html", + url: "http://10.9.64.88:8000/etherpad.html", name: "Etherpad", }, ], From f6f660fa9a240de088b8284ba41557660968438c Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 6 Jun 2017 23:45:17 +0100 Subject: [PATCH 18/69] Initial app icon tiles --- src/components/structures/AppWidget.js | 28 +++++++++++++ src/components/structures/ModularWidgets.js | 13 +++++++ src/components/views/dialogs/AddAppDialog.js | 18 ++++----- src/components/views/elements/AppIconTile.js | 41 ++++++++++++++++++++ src/components/views/rooms/AppsDrawer.js | 10 ++--- 5 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 src/components/structures/AppWidget.js create mode 100644 src/components/structures/ModularWidgets.js create mode 100644 src/components/views/elements/AppIconTile.js diff --git a/src/components/structures/AppWidget.js b/src/components/structures/AppWidget.js new file mode 100644 index 0000000000..5ab2207f60 --- /dev/null +++ b/src/components/structures/AppWidget.js @@ -0,0 +1,28 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import ModularWidgets from 'ModularWidgets'; + +class AppWidget { + constructor(type, url, options) { + if(!ModularWidgets.widgetTypes.includes(type) || url === "") { + return null; + } + this.type = type; + this.url = url; + this.options = options || {}; + } +} +export default AppWidget; diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js new file mode 100644 index 0000000000..16b00dfbf3 --- /dev/null +++ b/src/components/structures/ModularWidgets.js @@ -0,0 +1,13 @@ +class ModularWidgets { + static widgetTypes = [ + { + type: 'etherpad', + icon: '', + }, + { + type: 'grafana', + icon: '', + }, + ]; +} +export default ModularWidgets; diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index b72a3809b4..90bb53f3c3 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -53,22 +53,22 @@ export default React.createClass({ return (
- Please enter the URL of the app / widget to add. -
-
-
+ + {/*
+ +
Or enter the URL of the widget to add.
-
+
- -
- + +
*/} +
); }, diff --git a/src/components/views/elements/AppIconTile.js b/src/components/views/elements/AppIconTile.js new file mode 100644 index 0000000000..2c67efcede --- /dev/null +++ b/src/components/views/elements/AppIconTile.js @@ -0,0 +1,41 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; +import React from 'react'; + +class AppIconTile extends React.Component { + render() { + return ( +
+ {this.props.name} +
+

{this.props.name}

+

{this.props.description}

+
+
+ ); + } +} + +AppIconTile.propTypes = { + type: React.PropTypes.string.isRequired, + icon: React.PropTypes.string.isRequired, + name: React.PropTypes.string.isRequired, + description: React.PropTypes.string.isRequired, +}; + +export default AppIconTile; diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index af262a9470..0386128a10 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -32,7 +32,7 @@ const roomWidgetConfig = { }, { id: "recipie", - url: "http://10.9.64.88:8000/recepie.html", + url: "http://10.9.64.55:8000/recepie.html", name: "Ingredients - Boeuf Bourguignon", }, ], @@ -40,7 +40,7 @@ const roomWidgetConfig = { '!JWeMRscvtWqfNuzmSf:matrix.org': [ { id: "grafana", - url: "http://10.9.64.88:8000/grafana.html", + url: "http://10.9.64.55:8000/grafana.html", name: "Monitoring our Single-Point-Of-Failure DB", }, ], @@ -53,7 +53,7 @@ const roomWidgetConfig = { }, { id: "thermometer", - url: "http://10.9.64.88:8000/index.html", + url: "http://10.9.64.55:8000/index.html", name: "Tip Me!!! -- Send me cash $$$", }, ], @@ -66,7 +66,7 @@ const roomWidgetConfig = { }, { id: "thermometer", - url: "http://10.9.64.88:8000/index.html", + url: "http://10.9.64.55:8000/index.html", name: "Tip Me!!! -- Send me cash $$$", }, ], @@ -74,7 +74,7 @@ const roomWidgetConfig = { '!BLQjREzUgbtIsgrvRn:matrix.org': [ { id: "etherpad", - url: "http://10.9.64.88:8000/etherpad.html", + url: "http://10.9.64.55:8000/etherpad.html", name: "Etherpad", }, ], From c552f7f336f9490fd17dd1b3d4afb8c40e9d2f53 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 7 Jun 2017 10:55:49 +0100 Subject: [PATCH 19/69] App icon styling --- src/components/structures/ModularWidgets.js | 8 ++++++-- src/components/views/dialogs/AddAppDialog.js | 13 ++++++++++++- src/components/views/elements/AppIconTile.js | 4 +++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js index 16b00dfbf3..306a645a6f 100644 --- a/src/components/structures/ModularWidgets.js +++ b/src/components/structures/ModularWidgets.js @@ -2,11 +2,15 @@ class ModularWidgets { static widgetTypes = [ { type: 'etherpad', - icon: '', + icon: 'http://localhost:8000/static/etherpad.svg', + name: 'Etherpad', + description: 'Collaborative text editor', }, { type: 'grafana', - icon: '', + icon: 'http://localhost:8000/static/grafana.svg', + name: 'Grafana', + description: 'Graph and monitor all the things!', }, ]; } diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index 90bb53f3c3..0d24c641a6 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -16,6 +16,8 @@ limitations under the License. import React from 'react'; import sdk from '../../../index'; +import AppIconTile from '../elements/AppIconTile'; +import ModularWidgets from '../../structures/ModularWidgets'; /** * Prompt the user for address of iframe widget @@ -50,13 +52,22 @@ export default React.createClass({ render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const appCards = ModularWidgets.widgetTypes.map((widgetType, index) => + , + ); + return (
- + {appCards} {/*
Or enter the URL of the widget to add.
diff --git a/src/components/views/elements/AppIconTile.js b/src/components/views/elements/AppIconTile.js index 2c67efcede..9e9dbe6d41 100644 --- a/src/components/views/elements/AppIconTile.js +++ b/src/components/views/elements/AppIconTile.js @@ -21,7 +21,9 @@ class AppIconTile extends React.Component { render() { return (
- {this.props.name} +
+ {this.props.name} +

{this.props.name}

{this.props.description}

From a09001933a4c48610c1b39ff40f056a2e77ed226 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 7 Jun 2017 15:39:27 +0100 Subject: [PATCH 20/69] git status --- src/components/structures/ModularWidgets.js | 6 ++++++ src/components/views/dialogs/AddAppDialog.js | 4 ++-- src/components/views/elements/AppIconTile.js | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js index 306a645a6f..4e59fd9cfd 100644 --- a/src/components/structures/ModularWidgets.js +++ b/src/components/structures/ModularWidgets.js @@ -12,6 +12,12 @@ class ModularWidgets { name: 'Grafana', description: 'Graph and monitor all the things!', }, + { + type: 'custom', + icon: 'http://localhost:8000/static/blocks.png', + name: 'Custom Widget', + description: 'Add your own custom widget', + }, ]; } export default ModularWidgets; diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index 0d24c641a6..6d722365e8 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -68,7 +68,7 @@ export default React.createClass({ >
{appCards} - {/*
+
Or enter the URL of the widget to add.
-
*/} +
); diff --git a/src/components/views/elements/AppIconTile.js b/src/components/views/elements/AppIconTile.js index 9e9dbe6d41..282a33743c 100644 --- a/src/components/views/elements/AppIconTile.js +++ b/src/components/views/elements/AppIconTile.js @@ -18,9 +18,15 @@ limitations under the License. import React from 'react'; class AppIconTile extends React.Component { + render() { + const contentClasses = ['mx_AppIconTile']; + // if(this.props.type == 'custom') { + // contentClasses.push('mx_AppIconTile_active'); + // } + return ( -
+
{this.props.name}
From 6d1d43524788e8278681fcef2ba466ba96addeed Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 9 Jun 2017 11:21:37 +0100 Subject: [PATCH 21/69] Scalar: add in set_widget and get_widgets --- src/ScalarMessaging.js | 141 +++++++++++++++++++++++++++++++++++- src/i18n/strings/en_EN.json | 1 + 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index c1b975e8e8..2ceb021a93 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.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. @@ -109,6 +110,76 @@ Example: response: 78 } +set_widget +---------- +Set a new widget in the room. Clobbers based on the ID. + +Request: + - `room_id` (String) is the room to set the widget in. + - `widget_id` (String) is the ID of the widget to add (or replace if it already exists). + It can be an arbitrary UTF8 string and is purely for distinguishing between widgets. + - `url` (String) is the URL that clients should load in an iframe to run the widget. + All widgets must have a valid URL. If the URL is `null` (not `undefined`), the + widget will be removed from the room. + - `type` (String) is the type of widget, which is provided as a hint for matrix clients so they + can configure/lay out the widget in different ways. All widgets must have a type. + - `name` (String) is an optional human-readable string about the widget. + - `data` (Object) is some optional data about the widget, and can contain arbitrary key/value pairs. +Response: +{ + success: true +} +Example: +{ + action: "set_widget", + room_id: "!foo:bar", + widget_id: "abc123", + url: "http://widget.url", + type: "example", + response: { + success: true + } +} + +get_widgets +----------- +Get a list of all widgets in the room. The response is the `content` field +of the state event. + +Request: + - `room_id` (String) is the room to get the widgets in. +Response: +{ + $widget_id: { + type: "example", + url: "http://widget.url", + name: "Example Widget", + data: { + key: "val" + } + }, + $widget_id: { ... } +} +Example: +{ + action: "get_widgets", + room_id: "!foo:bar", + widget_id: "abc123", + url: "http://widget.url", + type: "example", + response: { + $widget_id: { + type: "example", + url: "http://widget.url", + name: "Example Widget", + data: { + key: "val" + } + }, + $widget_id: { ... } + } +} + membership_state AND bot_options -------------------------------- @@ -191,6 +262,68 @@ function inviteUser(event, roomId, userId) { }); } +function setWidget(event, roomId) { + // check required fields exist + const widgetId = event.data.widget_id; + const widgetType = event.data.type; + const widgetUrl = event.data.url; + if (!widgetId || widgetUrl === undefined || !widgetType) { + sendError(event, _t("Unable to create widget."), new Error("Missing required widget fields.")); + return; + } + const client = MatrixClientPeg.get(); + if (!client) { + sendError(event, _t('You need to be logged in.')); + return; + } + + // check types of fields + const widgetName = event.data.name; // optional + const widgetData = event.data.data; // optional + if (widgetName !== undefined && typeof widgetName !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Optional field 'name' must be a string.")); + return; + } + if (widgetData !== undefined && !(widgetData instanceof Object)) { + sendError(event, _t("Unable to create widget."), new Error("Optional field 'data' must be an Object.")); + return; + } + if (typeof widgetType !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Field 'type' must be a string.")); + return; + } + if (widgetUrl !== null && typeof widgetUrl !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Field 'url' must be a string or null.")); + return; + } + + // TODO: same dance we do for power levels. It'd be nice if the JS SDK had helper methods to do this. + client.getStateEvent(roomId, "im.vector.modular.widgets", "").then((widgets) => { + if (widgetUrl === null) { + delete widgets[widgetId]; + } + else { + widgets[widgetId] = { + type: widgetType, + url: widgetUrl, + name: widgetName, + data: widgetData, + }; + } + return client.sendStateEvent(roomId, "im.vector.modular.widgets", widgets); + }).done(() => { + sendResponse(event, { + success: true, + }); + }, (err) => { + sendError(event, _t('Failed to send request.'), err); + }); +} + +function getWidgets(event, roomId) { + returnStateEvent(event, roomId, "im.vector.modular.widgets", ""); +} + function setPlumbingState(event, roomId, status) { if (typeof status !== 'string') { throw new Error('Plumbing state status should be a string'); @@ -367,7 +500,7 @@ const onMessage = function(event) { return; } - // Getting join rules does not require userId + // These APIs don't require userId if (event.data.action === "join_rules_state") { getJoinRules(event, roomId); return; @@ -377,6 +510,12 @@ const onMessage = function(event) { } else if (event.data.action === "get_membership_count") { getMembershipCount(event, roomId); return; + } else if (event.data.action === "set_widget") { + setWidget(event, roomId); + return; + } else if (event.data.action === "get_widgets") { + getWidgets(event, roomId); + return; } if (!userId) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3540feddee..7494d9378f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -580,6 +580,7 @@ "Turn Markdown on": "Turn Markdown on", "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).", "Unable to add email address": "Unable to add email address", + "Unable to create widget.": "Unable to create widget.", "Unable to remove contact information": "Unable to remove contact information", "Unable to restore previous session": "Unable to restore previous session", "Unable to verify email address.": "Unable to verify email address.", From b70881f07831115128cd9247e0535f0810f6b4cf Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 9 Jun 2017 12:34:19 +0100 Subject: [PATCH 22/69] Rejig to support deletions better --- src/ScalarMessaging.js | 46 +++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 2ceb021a93..61a76289b6 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -263,38 +263,42 @@ function inviteUser(event, roomId, userId) { } function setWidget(event, roomId) { - // check required fields exist const widgetId = event.data.widget_id; const widgetType = event.data.type; const widgetUrl = event.data.url; - if (!widgetId || widgetUrl === undefined || !widgetType) { - sendError(event, _t("Unable to create widget."), new Error("Missing required widget fields.")); - return; - } + const widgetName = event.data.name; // optional + const widgetData = event.data.data; // optional + const client = MatrixClientPeg.get(); if (!client) { sendError(event, _t('You need to be logged in.')); return; } - // check types of fields - const widgetName = event.data.name; // optional - const widgetData = event.data.data; // optional - if (widgetName !== undefined && typeof widgetName !== 'string') { - sendError(event, _t("Unable to create widget."), new Error("Optional field 'name' must be a string.")); + // both adding/removing widgets need these checks + if (!widgetId || widgetUrl === undefined) { + sendError(event, _t("Unable to create widget."), new Error("Missing required widget fields.")); return; } - if (widgetData !== undefined && !(widgetData instanceof Object)) { - sendError(event, _t("Unable to create widget."), new Error("Optional field 'data' must be an Object.")); - return; - } - if (typeof widgetType !== 'string') { - sendError(event, _t("Unable to create widget."), new Error("Field 'type' must be a string.")); - return; - } - if (widgetUrl !== null && typeof widgetUrl !== 'string') { - sendError(event, _t("Unable to create widget."), new Error("Field 'url' must be a string or null.")); - return; + + if (widgetUrl !== null) { // if url is null it is being deleted, don't need to check name/type/etc + // check types of fields + if (widgetName !== undefined && typeof widgetName !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Optional field 'name' must be a string.")); + return; + } + if (widgetData !== undefined && !(widgetData instanceof Object)) { + sendError(event, _t("Unable to create widget."), new Error("Optional field 'data' must be an Object.")); + return; + } + if (typeof widgetType !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Field 'type' must be a string.")); + return; + } + if (typeof widgetUrl !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Field 'url' must be a string or null.")); + return; + } } // TODO: same dance we do for power levels. It'd be nice if the JS SDK had helper methods to do this. From 34d7d793b7d0208be49df0de24a9e95225d88a84 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 9 Jun 2017 15:06:09 +0100 Subject: [PATCH 23/69] Handle M_NOT_FOUND --- src/ScalarMessaging.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 61a76289b6..49f1a5c6f9 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -315,6 +315,18 @@ function setWidget(event, roomId) { }; } return client.sendStateEvent(roomId, "im.vector.modular.widgets", widgets); + }, (err) => { + if (err.errcode === "M_NOT_FOUND") { + return client.sendStateEvent(roomId, "im.vector.modular.widgets", { + [widgetId]: { + type: widgetType, + url: widgetUrl, + name: widgetName, + data: widgetData, + } + }); + } + throw err; }).done(() => { sendResponse(event, { success: true, From b893887707079ac23e2312a59ad7edf846a718e9 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 12 Jun 2017 14:52:41 +0100 Subject: [PATCH 24/69] Fix merge conflict --- src/components/views/rooms/MessageComposer.js | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 0f9ec5cd4e..c4e13a5fe6 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -354,26 +354,21 @@ export default class MessageComposer extends React.Component { } else { controls.push(
-<<<<<<< HEAD - You do not have permission to post to this room -
, -======= { _t('You do not have permission to post to this room') } -
->>>>>>> 31f1e421f226bd471b68cdf1f69a8e049a443e5d +
, ); } - // let autoComplete; - // if (UserSettingsStore.isFeatureEnabled('rich_text_editor')) { - // autoComplete =
- // - //
; - // } + let autoComplete; + if (UserSettingsStore.isFeatureEnabled('rich_text_editor')) { + autoComplete =
+ +
; + } const {style, blockType} = this.state.inputState; From 2da30137ec5bc7ef1230c5560fc9f221d9b6fec5 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 13 Jun 2017 10:31:16 +0100 Subject: [PATCH 25/69] Fix import path and add LG widget --- src/components/structures/AppWidget.js | 2 +- src/components/views/rooms/AppsDrawer.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/structures/AppWidget.js b/src/components/structures/AppWidget.js index 5ab2207f60..283efb9fcd 100644 --- a/src/components/structures/AppWidget.js +++ b/src/components/structures/AppWidget.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import ModularWidgets from 'ModularWidgets'; +import ModularWidgets from './ModularWidgets'; class AppWidget { constructor(type, url, options) { diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 0386128a10..9c49fdb663 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -78,6 +78,14 @@ const roomWidgetConfig = { name: "Etherpad", }, ], + // Insurance room - !nTUetaZELiqWcWYshy:matrix.org + '!nTUetaZELiqWcWYshy:matrix.org': [ + { + id: "lg", + url: "http://localhost:8000/lg.html", + name: "L&G Insurance Policy", + }, + ], }; module.exports = React.createClass({ From 99b1de7f0e886166a50885418cb3e021e5a0de7e Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jun 2017 15:19:06 +0200 Subject: [PATCH 26/69] RoomView: Display AppsDrawer if apps in room state --- src/components/structures/RoomView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 29534727a2..4d7ac46ab4 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -251,12 +251,18 @@ module.exports = React.createClass({ } else if (isUserJoined) { MatrixClientPeg.get().stopPeeking(); this.setState({ + showApps: this._shouldShowApps(room), unsentMessageError: this._getUnsentMessageError(room), }); this._onRoomLoaded(room); } }, + _shouldShowApps: function(room) { + const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets', ''); + return appsStateEvents && Object.keys(appsStateEvents.getContent()).length > 0; + }, + componentDidMount: function() { var call = this._getCallForRoom(); var callState = call ? call.call_state : "ended"; From e2759774fc027a7d4057571955217e6182fd43b8 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jun 2017 15:19:38 +0200 Subject: [PATCH 27/69] RoomView: Correctly pass userId from matrix client It isn't set in the state anywhere. --- 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 4d7ac46ab4..0bf3d4e181 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1604,7 +1604,7 @@ module.exports = React.createClass({ var auxPanel = ( Date: Tue, 13 Jun 2017 15:28:37 +0200 Subject: [PATCH 28/69] AppsDrawer: Populate apps from room state --- src/components/views/rooms/AppsDrawer.js | 155 +++++++++++------------ 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 0386128a10..d2b6cf8b21 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -17,107 +17,100 @@ limitations under the License. 'use strict'; const React = require('react'); +const MatrixClientPeg = require('../../../MatrixClientPeg'); const AddAppDialog = require('../dialogs/AddAppDialog'); const AppTile = require('../elements/AppTile'); const Modal = require("../../../Modal"); - -// FIXME -- Hard coded widget config -const roomWidgetConfig = { - // Cooking room - '!IAkkwswSrOSzPRWksX:matrix.org': [ - { - id: "youtube", - url: "https://www.youtube.com/embed/ZJy1ajvMU1k?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", - name: "Live stream - Boeuf Bourguignon", - }, - { - id: "recipie", - url: "http://10.9.64.55:8000/recepie.html", - name: "Ingredients - Boeuf Bourguignon", - }, - ], - // Grafana room - '!JWeMRscvtWqfNuzmSf:matrix.org': [ - { - id: "grafana", - url: "http://10.9.64.55:8000/grafana.html", - name: "Monitoring our Single-Point-Of-Failure DB", - }, - ], - // Camgirl room - https://www.youtube.com/watch?v=ZfkwW4GgAiU - '!wQqrqwOipOOWALxJNe:matrix.org': [ - { - id: "youtube", - url: "https://www.youtube.com/embed/ZfkwW4GgAiU?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", - name: "Live stream - ChatGirl86", - }, - { - id: "thermometer", - url: "http://10.9.64.55:8000/index.html", - name: "Tip Me!!! -- Send me cash $$$", - }, - ], - // Game room - https://www.youtube.com/watch?v=Dm2Ma1dOFO4 - '!dYSCwtVljhTdBlgNxq:matrix.org': [ - { - id: "youtube", - url: "https://www.youtube.com/embed/Dm2Ma1dOFO4?controls=0&enablejsapi=1&iv_load_policy=3&modestbranding=1&playsinline=1&autoplay=1", - name: "Live stream - Overwatch Balle Royale", - }, - { - id: "thermometer", - url: "http://10.9.64.55:8000/index.html", - name: "Tip Me!!! -- Send me cash $$$", - }, - ], - // Game room - !BLQjREzUgbtIsgrvRn:matrix.org - '!BLQjREzUgbtIsgrvRn:matrix.org': [ - { - id: "etherpad", - url: "http://10.9.64.55:8000/etherpad.html", - name: "Etherpad", - }, - ], -}; +const dis = require('../../../dispatcher'); module.exports = React.createClass({ displayName: 'AppsDrawer', propTypes: { + room: React.PropTypes.object.isRequired, + }, + + componentWillMount: function() { + MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents); }, componentDidMount: function() { }, - initAppConfig: function(appConfig) { - console.log("App props: ", this.props); - appConfig = appConfig.map( - (app, index, arr) => { - switch(app.id) { - case 'etherpad': - app.url = app.url + '?userName=' + this.props.userId + - '&padId=' + this.props.room.roomId; - break; - } + componentWillUnmount: function() { + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); + } + }, - return app; - }); - return appConfig; + _initAppConfig: function(appId, app) { + console.log("App props: ", this.props); + app.id = appId; + app.name = app.type; + + switch(app.type) { + case 'etherpad': + app.url = app.url + '?userName=' + this.props.userId + + '&padId=' + this.props.room.roomId; + break; + case 'jitsi': { + const user = MatrixClientPeg.get().getUser(this.props.userId); + app.url = app.url + + '?confId=' + app.data.confId + + '&displayName=' + encodeURIComponent(user.displayName) + + '&avatarUrl=' + encodeURIComponent(MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl)) + + '&email=' + encodeURIComponent(this.props.userId) + + '&isAudioConf=' + app.data.isAudioConf; + + app.name += ' - ' + app.data.confId; + break; + } + } + + return app; }, getInitialState: function() { - for (const key in roomWidgetConfig) { - if(key == this.props.room.roomId) { - return { - apps: this.initAppConfig(roomWidgetConfig[key]), - }; - } - } return { - apps: [], + apps: this._getApps(), }; }, + onRoomStateEvents: function(ev, state) { + if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'im.vector.modular.widgets') { + return; + } + this._updateApps(); + }, + + _getApps: function() { + const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + if (!appsStateEvents) { + return []; + } + const appsStateEvent = appsStateEvents.getContent(); + if (Object.keys(appsStateEvent).length < 1) { + return []; + } + + return Object.keys(appsStateEvent).map((appId) => { + return this._initAppConfig(appId, appsStateEvent[appId]); + }); + }, + + _updateApps: function() { + const apps = this._getApps(); + if (apps.length < 1) { + dis.dispatch({ + action: 'appsDrawer', + show: false, + }); + } + this.setState({ + apps: this._getApps(), + }); + }, + onClickAddWidget: function() { Modal.createDialog(AddAppDialog, { onFinished: (proceed, reason) => { @@ -131,7 +124,7 @@ module.exports = React.createClass({ render: function() { const apps = this.state.apps.map( (app, index, arr) => Date: Tue, 13 Jun 2017 15:31:37 +0200 Subject: [PATCH 29/69] AddAppDialog: Support adding apps to room state --- src/components/views/dialogs/AddAppDialog.js | 9 +++- src/components/views/elements/AppIconTile.js | 11 +++- src/components/views/rooms/AppsDrawer.js | 55 ++++++++++++++++++-- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index 6d722365e8..49e16820d6 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -46,10 +46,14 @@ export default React.createClass({ onFormSubmit: function(ev) { ev.preventDefault(); - this.props.onFinished(true, this.state.value); + this.props.onFinished(true, 'custom', this.state.value); return false; }, + onTileClick: function(value) { + this.props.onFinished(true, value, null); + }, + render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const appCards = ModularWidgets.widgetTypes.map((widgetType, index) => @@ -58,7 +62,8 @@ export default React.createClass({ type={widgetType.type} icon={widgetType.icon} name={widgetType.name} - description={widgetType.description}/>, + description={widgetType.description} + onClick={this.onTileClick}/>, ); return ( diff --git a/src/components/views/elements/AppIconTile.js b/src/components/views/elements/AppIconTile.js index 282a33743c..32fcd74111 100644 --- a/src/components/views/elements/AppIconTile.js +++ b/src/components/views/elements/AppIconTile.js @@ -18,6 +18,14 @@ limitations under the License. import React from 'react'; class AppIconTile extends React.Component { + constructor(props) { + super(props); + this._onTileClick = this._onTileClick.bind(this); + } + + _onTileClick(props) { + this.props.onClick(this.props.type); + } render() { const contentClasses = ['mx_AppIconTile']; @@ -26,7 +34,7 @@ class AppIconTile extends React.Component { // } return ( -
+
{this.props.name}
@@ -44,6 +52,7 @@ AppIconTile.propTypes = { icon: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired, description: React.PropTypes.string.isRequired, + onClick: React.PropTypes.func.isRequired, }; export default AppIconTile; diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index d2b6cf8b21..1dee5dacf8 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -113,10 +113,59 @@ module.exports = React.createClass({ onClickAddWidget: function() { Modal.createDialog(AddAppDialog, { - onFinished: (proceed, reason) => { - if (!proceed) return; + onFinished: (proceed, type, value) => { + if (!proceed || !type) return; + if (type === 'custom' && !value) return; - this.state.apps.push(); + const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + let appsStateEvent = {}; + if (appsStateEvents) { + appsStateEvent = appsStateEvents.getContent(); + } + + if (appsStateEvent[type]) { + return; + } + + switch (type) { + case 'etherpad': + appsStateEvent.etherpad = { + type: type, + url: 'http://localhost:8000/etherpad.html', + }; + break; + case 'grafana': + appsStateEvent.grafana = { + type: type, + url: 'http://localhost:8000/grafana.html', + }; + break; + case 'jitsi': + appsStateEvent.videoConf = { + type: type, + url: 'http://localhost:8000/jitsi.html', + data: { + confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), + }, + }; + break; + case 'custom': + appsStateEvent.custom = { + type: type, + url: value, + }; + break; + default: + console.warn('Unsupported app type:', type); + return; + } + + MatrixClientPeg.get().sendStateEvent( + this.props.room.roomId, + 'im.vector.modular.widgets', + appsStateEvent, + '', + ); }, }); }, From bcb2f8408b80e1e9898e27e80ea77f2d99a51142 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jun 2017 15:32:40 +0200 Subject: [PATCH 30/69] AppTile: Fix typo in property name --- src/components/views/elements/AppTile.js | 2 +- src/components/views/rooms/AppsDrawer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index a3713d2b96..90208cd548 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -53,7 +53,7 @@ export default React.createClass({ render: function() { return ( -
+
{this.props.name} diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 1dee5dacf8..504221f894 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -177,8 +177,8 @@ module.exports = React.createClass({ id={app.id} url={app.url} name={app.name} - fullWdith={arr.length<2 ? true : false} roomId={this.props.roomId} + fullWidth={arr.length<2 ? true : false} userId={this.props.userId} />); From b63edcb390e6bfafaaa6b1771f7b3ec319807134 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jun 2017 15:33:17 +0200 Subject: [PATCH 31/69] AppTile: Support deletion of apps from room state --- src/components/views/elements/AppTile.js | 20 ++++++++++++++++++++ src/components/views/rooms/AppsDrawer.js | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 90208cd548..3f81ff5067 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; const React = require('react'); +const MatrixClientPeg = require('../../../MatrixClientPeg'); export default React.createClass({ displayName: 'AppTile', @@ -25,6 +26,7 @@ export default React.createClass({ id: React.PropTypes.string.isRequired, url: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired, + room: React.PropTypes.object.isRequired, }, getDefaultProps: function() { @@ -49,6 +51,24 @@ export default React.createClass({ _onDeleteClick: function() { console.log("Delete widget %s", this.props.id); + const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + if (!appsStateEvents) { + return; + } + const appsStateEvent = appsStateEvents.getContent(); + if (appsStateEvent[this.props.id]) { + delete appsStateEvent[this.props.id]; + MatrixClientPeg.get().sendStateEvent( + this.props.room.roomId, + 'im.vector.modular.widgets', + appsStateEvent, + '', + ).then(() => { + console.log('Deleted widget'); + }, (e) => { + console.error('Failed to delete widget', e); + }); + } }, render: function() { diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 504221f894..816b813da1 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -177,8 +177,8 @@ module.exports = React.createClass({ id={app.id} url={app.url} name={app.name} - roomId={this.props.roomId} fullWidth={arr.length<2 ? true : false} + room={this.props.room} userId={this.props.userId} />); From 5d898dd0984663e1ccfff4c983107c76397bb597 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jun 2017 15:34:05 +0200 Subject: [PATCH 32/69] AuxPanel: Add type checking for userId and showApps properties --- src/components/views/rooms/AuxPanel.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index efa4da141f..4958de263e 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -29,6 +29,8 @@ module.exports = React.createClass({ propTypes: { // js-sdk room object room: React.PropTypes.object.isRequired, + userId: React.PropTypes.string.isRequired, + showApps: React.PropTypes.bool, // Conference Handler implementation conferenceHandler: React.PropTypes.object, From 91eabbba604faed2e062ff777d71c12763c2d9de Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jun 2017 15:35:13 +0200 Subject: [PATCH 33/69] MessageComposer: Trigger Jitsi app from call buttons --- src/components/structures/ModularWidgets.js | 6 +++ src/components/views/elements/AppTile.js | 2 +- src/components/views/rooms/MessageComposer.js | 40 ++++++++++++++----- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js index 4e59fd9cfd..b459241948 100644 --- a/src/components/structures/ModularWidgets.js +++ b/src/components/structures/ModularWidgets.js @@ -12,6 +12,12 @@ class ModularWidgets { name: 'Grafana', description: 'Graph and monitor all the things!', }, + { + type: 'jitsi', + icon: 'http://localhost:8000/static/jitsi.png', + name: 'jitsi', + description: 'Jitsi video conference', + }, { type: 'custom', icon: 'http://localhost:8000/static/blocks.png', diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 3f81ff5067..3bf99dbddd 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -94,7 +94,7 @@ export default React.createClass({
- +
); diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 63f980304e..24254d989b 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -153,21 +153,41 @@ export default class MessageComposer extends React.Component { }); } - onCallClick(ev) { - console.warn("Call but clicked!"); + _startCallApp(isAudioConf) { dis.dispatch({ - action: 'place_call', - type: ev.shiftKey ? "screensharing" : "video", - room_id: this.props.room.roomId, + action: 'appsDrawer', + show: true, }); + + const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + let appsStateEvent = {}; + if (appsStateEvents) { + appsStateEvent = appsStateEvents.getContent(); + } + if (!appsStateEvent.videoConf) { + appsStateEvent.videoConf = { + type: 'jitsi', + url: 'http://localhost:8000/jitsi.html', + data: { + confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), + isAudioConf: isAudioConf, + }, + }; + MatrixClientPeg.get().sendStateEvent( + this.props.room.roomId, + 'im.vector.modular.widgets', + appsStateEvent, + '', + ).then(() => console.log('Sent state'), (e) => console.error(e)); + } + } + + onCallClick(ev) { + this._startCallApp(false); } onVoiceCallClick(ev) { - dis.dispatch({ - action: 'place_call', - type: 'voice', - room_id: this.props.room.roomId, - }); + this._startCallApp(true); } onShowAppsClick(ev) { From 880e7149f345decd0f2c9ded04b3565ccfdfda4e Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 14 Jun 2017 13:05:43 +0200 Subject: [PATCH 34/69] ModularWidgets: Add a quick VR demo widget --- src/components/structures/ModularWidgets.js | 6 ++++++ src/components/views/rooms/AppsDrawer.js | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js index b459241948..314d273103 100644 --- a/src/components/structures/ModularWidgets.js +++ b/src/components/structures/ModularWidgets.js @@ -18,6 +18,12 @@ class ModularWidgets { name: 'jitsi', description: 'Jitsi video conference', }, + { + type: 'vrdemo', + icon: 'http://localhost:8000/static/jitsi.png', + name: 'vrdemo', + description: 'Matrix VR Demo', + }, { type: 'custom', icon: 'http://localhost:8000/static/blocks.png', diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 816b813da1..1d738fb9d2 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -65,6 +65,9 @@ module.exports = React.createClass({ app.name += ' - ' + app.data.confId; break; } + case 'vrdemo': + app.name = 'Matrix VR Demo'; + break; } return app; @@ -149,6 +152,12 @@ module.exports = React.createClass({ }, }; break; + case 'vrdemo': + appsStateEvent.vrDemo = { + type: type, + url: 'http://localhost:8000/vrdemo.html', + }; + break; case 'custom': appsStateEvent.custom = { type: type, From 9c8ab2691b1cdc3f3c061bd1b049b208f590fd2b Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 14 Jun 2017 13:26:43 +0200 Subject: [PATCH 35/69] AppsDrawer: Only append queryParams once --- src/components/views/rooms/AppsDrawer.js | 29 ++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 1d738fb9d2..a917dde5f6 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -50,13 +50,12 @@ module.exports = React.createClass({ switch(app.type) { case 'etherpad': - app.url = app.url + '?userName=' + this.props.userId + + app.queryParams = '?userName=' + this.props.userId + '&padId=' + this.props.room.roomId; break; case 'jitsi': { const user = MatrixClientPeg.get().getUser(this.props.userId); - app.url = app.url + - '?confId=' + app.data.confId + + app.queryParams = '?confId=' + app.data.confId + '&displayName=' + encodeURIComponent(user.displayName) + '&avatarUrl=' + encodeURIComponent(MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl)) + '&email=' + encodeURIComponent(this.props.userId) + @@ -181,15 +180,21 @@ module.exports = React.createClass({ render: function() { const apps = this.state.apps.map( - (app, index, arr) => ); + (app, index, arr) => { + let appUrl = app.url; + if (app.queryParams) { + appUrl += app.queryParams; + } + return ; + }); const addWidget = this.state.apps && this.state.apps.length < 2 && (
Date: Wed, 14 Jun 2017 13:27:15 +0200 Subject: [PATCH 36/69] AppsDrawer: Generate room alias for vrdemo --- src/components/views/rooms/AppsDrawer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index a917dde5f6..c132b395cd 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -65,7 +65,8 @@ module.exports = React.createClass({ break; } case 'vrdemo': - app.name = 'Matrix VR Demo'; + app.name = 'Matrix VR Demo - ' + app.data.roomAlias; + app.queryParams = '?roomAlias=' + encodeURIComponent(app.data.roomAlias); break; } @@ -155,6 +156,9 @@ module.exports = React.createClass({ appsStateEvent.vrDemo = { type: type, url: 'http://localhost:8000/vrdemo.html', + data: { + roomAlias: '#vrvc' + this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), + }, }; break; case 'custom': From 5f020423bc89f1c82a9efb7511827f6c46966d8d Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 14 Jun 2017 15:05:11 +0200 Subject: [PATCH 37/69] AddAppDialog: Put the submit button inside the form --- src/components/views/dialogs/AddAppDialog.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index 49e16820d6..512570d9ad 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -80,10 +80,10 @@ export default React.createClass({ autoFocus={true} onChange={this.onValueChange} size="30" className="mx_SetAppURLDialog_input" /> +
+ +
-
- -
); From edb11d805e9f5e5038c12a3cb80f09990af61230 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 14 Jun 2017 15:05:29 +0200 Subject: [PATCH 38/69] AppsDrawer: Open add app widget if opening empty drawer This felt much better than having to also click the add app widget button. --- src/components/views/rooms/AppsDrawer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index c132b395cd..6daa4d98cf 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -35,6 +35,9 @@ module.exports = React.createClass({ }, componentDidMount: function() { + if (this.state.apps && this.state.apps.length < 1) { + this.onClickAddWidget(); + } }, componentWillUnmount: function() { From 9d89bfe26437a52f96ea4443cb57082f53335aea Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 19 Jun 2017 11:36:15 +0100 Subject: [PATCH 39/69] Fix jitsi logo references --- src/components/structures/ModularWidgets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js index 314d273103..7beeba157c 100644 --- a/src/components/structures/ModularWidgets.js +++ b/src/components/structures/ModularWidgets.js @@ -14,13 +14,13 @@ class ModularWidgets { }, { type: 'jitsi', - icon: 'http://localhost:8000/static/jitsi.png', + icon: 'http://localhost:8000/static/jitsi.svg', name: 'jitsi', description: 'Jitsi video conference', }, { type: 'vrdemo', - icon: 'http://localhost:8000/static/jitsi.png', + icon: 'http://localhost:8000/static/jitsi.svg', name: 'vrdemo', description: 'Matrix VR Demo', }, From f676b58c00d10f2865b035bee9744326e910f06c Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 19 Jun 2017 11:47:03 +0100 Subject: [PATCH 40/69] Add google ardboard logo --- src/components/structures/ModularWidgets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js index 7beeba157c..7d08401d52 100644 --- a/src/components/structures/ModularWidgets.js +++ b/src/components/structures/ModularWidgets.js @@ -20,7 +20,7 @@ class ModularWidgets { }, { type: 'vrdemo', - icon: 'http://localhost:8000/static/jitsi.svg', + icon: 'http://localhost:8000/static/cardboard.png', name: 'vrdemo', description: 'Matrix VR Demo', }, From 80c5a58fc3a74034bc1be3eb9b14b0ae73c777b1 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Mon, 19 Jun 2017 12:06:16 +0100 Subject: [PATCH 41/69] Fix custom widget form styling --- src/components/views/dialogs/AddAppDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js index 512570d9ad..35372009fc 100644 --- a/src/components/views/dialogs/AddAppDialog.js +++ b/src/components/views/dialogs/AddAppDialog.js @@ -74,7 +74,7 @@ export default React.createClass({
{appCards}
-
+
Or enter the URL of the widget to add.
Date: Tue, 20 Jun 2017 10:54:41 +0100 Subject: [PATCH 42/69] Replace add app dialog with scalar interface --- src/components/views/dialogs/AddAppDialog.js | 91 ---------- src/components/views/rooms/AppsDrawer.js | 180 +++++++++++-------- 2 files changed, 109 insertions(+), 162 deletions(-) delete mode 100644 src/components/views/dialogs/AddAppDialog.js diff --git a/src/components/views/dialogs/AddAppDialog.js b/src/components/views/dialogs/AddAppDialog.js deleted file mode 100644 index 35372009fc..0000000000 --- a/src/components/views/dialogs/AddAppDialog.js +++ /dev/null @@ -1,91 +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 AppIconTile from '../elements/AppIconTile'; -import ModularWidgets from '../../structures/ModularWidgets'; - -/** - * Prompt the user for address of iframe widget - * - * On success, `onFinished(true, newAppWidget)` is called. - */ -export default React.createClass({ - displayName: 'AddAppDialog', - propTypes: { - onFinished: React.PropTypes.func.isRequired, - }, - - getInitialState: function() { - return { - value: "", - }; - }, - - 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, 'custom', this.state.value); - return false; - }, - - onTileClick: function(value) { - this.props.onFinished(true, value, null); - }, - - render: function() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const appCards = ModularWidgets.widgetTypes.map((widgetType, index) => - , - ); - - return ( - -
- {appCards} -
- -
Or enter the URL of the widget to add.
- -
- -
- -
-
- ); - }, -}); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 6daa4d98cf..3a9b456c99 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -16,12 +16,14 @@ limitations under the License. 'use strict'; -const React = require('react'); -const MatrixClientPeg = require('../../../MatrixClientPeg'); -const AddAppDialog = require('../dialogs/AddAppDialog'); -const AppTile = require('../elements/AppTile'); -const Modal = require("../../../Modal"); -const dis = require('../../../dispatcher'); +import React from 'react'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import AppTile from '../elements/AppTile'; +import Modal from '../../../Modal'; +import dis from '../../../dispatcher'; +import sdk from '../../../index'; +import SdkConfig from '../../../SdkConfig'; +import ScalarAuthClient from '../../../ScalarAuthClient'; module.exports = React.createClass({ displayName: 'AppsDrawer', @@ -35,8 +37,20 @@ module.exports = React.createClass({ }, componentDidMount: function() { - if (this.state.apps && this.state.apps.length < 1) { - this.onClickAddWidget(); + this.scalarClient = null; + const appsDrawer = this; + if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) { + this.scalarClient = new ScalarAuthClient(); + this.scalarClient.connect().done(() => { + this.forceUpdate(); + if (appsDrawer.state.apps && appsDrawer.state.apps.length < 1) { + appsDrawer.onClickAddWidget(); + } + }, (err) => { + this.setState({ + scalar_error: err, + }); + }); } }, @@ -117,72 +131,96 @@ module.exports = React.createClass({ }); }, - onClickAddWidget: function() { - Modal.createDialog(AddAppDialog, { - onFinished: (proceed, type, value) => { - if (!proceed || !type) return; - if (type === 'custom' && !value) return; + onClickAddWidget: function(e) { + // Modal.createDialog(AddAppDialog, { + // onFinished: (proceed, type, value) => { + // if (!proceed || !type) return; + // if (type === 'custom' && !value) return; + // + // const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + // let appsStateEvent = {}; + // if (appsStateEvents) { + // appsStateEvent = appsStateEvents.getContent(); + // } + // + // if (appsStateEvent[type]) { + // return; + // } + // + // switch (type) { + // case 'etherpad': + // appsStateEvent.etherpad = { + // type: type, + // url: 'http://localhost:8000/etherpad.html', + // }; + // break; + // case 'grafana': + // appsStateEvent.grafana = { + // type: type, + // url: 'http://localhost:8000/grafana.html', + // }; + // break; + // case 'jitsi': + // appsStateEvent.videoConf = { + // type: type, + // url: 'http://localhost:8000/jitsi.html', + // data: { + // confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), + // }, + // }; + // break; + // case 'vrdemo': + // appsStateEvent.vrDemo = { + // type: type, + // url: 'http://localhost:8000/vrdemo.html', + // data: { + // roomAlias: '#vrvc' + this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), + // }, + // }; + // break; + // case 'custom': + // appsStateEvent.custom = { + // type: type, + // url: value, + // }; + // break; + // default: + // console.warn('Unsupported app type:', type); + // return; + // } + // + // MatrixClientPeg.get().sendStateEvent( + // this.props.room.roomId, + // 'im.vector.modular.widgets', + // appsStateEvent, + // '', + // ); + // }, + // }); - const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); - let appsStateEvent = {}; - if (appsStateEvents) { - appsStateEvent = appsStateEvents.getContent(); + if (e) { + e.preventDefault(); + } + + const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); + console.warn("scalarClient: ", this.scalarClient); + console.warn("hasCredentials: ", this.scalarClient.hasCredentials()); + console.warn("roomId: ", this.props.room.roomId); + console.warn("Scalar interface: ", this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId)); + const src = (this.scalarClient !== null && this.scalarClient.hasCredentials()) ? + this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId) : + null; + console.warn("src: ", src); + Modal.createDialog(IntegrationsManager, { + src: src, + onFinished: ()=>{ + if (this._calcSavePromises().length === 0) { + if (e) { + this.props.onCancelClick(e); + } } - - if (appsStateEvent[type]) { - return; - } - - switch (type) { - case 'etherpad': - appsStateEvent.etherpad = { - type: type, - url: 'http://localhost:8000/etherpad.html', - }; - break; - case 'grafana': - appsStateEvent.grafana = { - type: type, - url: 'http://localhost:8000/grafana.html', - }; - break; - case 'jitsi': - appsStateEvent.videoConf = { - type: type, - url: 'http://localhost:8000/jitsi.html', - data: { - confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), - }, - }; - break; - case 'vrdemo': - appsStateEvent.vrDemo = { - type: type, - url: 'http://localhost:8000/vrdemo.html', - data: { - roomAlias: '#vrvc' + this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), - }, - }; - break; - case 'custom': - appsStateEvent.custom = { - type: type, - url: value, - }; - break; - default: - console.warn('Unsupported app type:', type); - return; - } - - MatrixClientPeg.get().sendStateEvent( - this.props.room.roomId, - 'im.vector.modular.widgets', - appsStateEvent, - '', - ); }, - }); + }, "mx_IntegrationsManager"); }, render: function() { From 68473e118f09b8d34ea2dc010b315fb5bab0e267 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 20 Jun 2017 17:56:45 +0100 Subject: [PATCH 43/69] Ensure that Scalar Messaging is started and stopped on component mount / unmount --- src/components/views/rooms/AppsDrawer.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 3a9b456c99..c7b5ddbee6 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -24,6 +24,7 @@ import dis from '../../../dispatcher'; import sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import ScalarAuthClient from '../../../ScalarAuthClient'; +import ScalarMessaging from '../../../ScalarMessaging'; module.exports = React.createClass({ displayName: 'AppsDrawer', @@ -33,6 +34,7 @@ module.exports = React.createClass({ }, componentWillMount: function() { + ScalarMessaging.startListening(); MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents); }, @@ -55,6 +57,7 @@ module.exports = React.createClass({ }, componentWillUnmount: function() { + ScalarMessaging.startListening(); if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); } @@ -214,10 +217,8 @@ module.exports = React.createClass({ Modal.createDialog(IntegrationsManager, { src: src, onFinished: ()=>{ - if (this._calcSavePromises().length === 0) { - if (e) { - this.props.onCancelClick(e); - } + if (e) { + this.props.onCancelClick(e); } }, }, "mx_IntegrationsManager"); From 2cb2c44bd86332e8cfe6a26d9a32d070dc8da75c Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 20 Jun 2017 17:57:48 +0100 Subject: [PATCH 44/69] Remove unused logging --- src/components/views/rooms/AppsDrawer.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index c7b5ddbee6..0047e2fddc 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -206,14 +206,9 @@ module.exports = React.createClass({ } const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); - console.warn("scalarClient: ", this.scalarClient); - console.warn("hasCredentials: ", this.scalarClient.hasCredentials()); - console.warn("roomId: ", this.props.room.roomId); - console.warn("Scalar interface: ", this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId)); const src = (this.scalarClient !== null && this.scalarClient.hasCredentials()) ? this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId) : null; - console.warn("src: ", src); Modal.createDialog(IntegrationsManager, { src: src, onFinished: ()=>{ From e343e993557f3cc3651967611a846efc29c35899 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 11:28:38 +0100 Subject: [PATCH 45/69] Cleaned up unused files and removed commented code --- src/components/structures/AppWidget.js | 28 ------ src/components/structures/ModularWidgets.js | 35 ------- src/components/views/elements/AppIconTile.js | 58 ------------ src/components/views/elements/AppTile.js | 14 +-- src/components/views/elements/MemberTile.js | 97 -------------------- src/components/views/rooms/AppsDrawer.js | 2 +- 6 files changed, 3 insertions(+), 231 deletions(-) delete mode 100644 src/components/structures/AppWidget.js delete mode 100644 src/components/structures/ModularWidgets.js delete mode 100644 src/components/views/elements/AppIconTile.js delete mode 100644 src/components/views/elements/MemberTile.js diff --git a/src/components/structures/AppWidget.js b/src/components/structures/AppWidget.js deleted file mode 100644 index 283efb9fcd..0000000000 --- a/src/components/structures/AppWidget.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -import ModularWidgets from './ModularWidgets'; - -class AppWidget { - constructor(type, url, options) { - if(!ModularWidgets.widgetTypes.includes(type) || url === "") { - return null; - } - this.type = type; - this.url = url; - this.options = options || {}; - } -} -export default AppWidget; diff --git a/src/components/structures/ModularWidgets.js b/src/components/structures/ModularWidgets.js deleted file mode 100644 index 7d08401d52..0000000000 --- a/src/components/structures/ModularWidgets.js +++ /dev/null @@ -1,35 +0,0 @@ -class ModularWidgets { - static widgetTypes = [ - { - type: 'etherpad', - icon: 'http://localhost:8000/static/etherpad.svg', - name: 'Etherpad', - description: 'Collaborative text editor', - }, - { - type: 'grafana', - icon: 'http://localhost:8000/static/grafana.svg', - name: 'Grafana', - description: 'Graph and monitor all the things!', - }, - { - type: 'jitsi', - icon: 'http://localhost:8000/static/jitsi.svg', - name: 'jitsi', - description: 'Jitsi video conference', - }, - { - type: 'vrdemo', - icon: 'http://localhost:8000/static/cardboard.png', - name: 'vrdemo', - description: 'Matrix VR Demo', - }, - { - type: 'custom', - icon: 'http://localhost:8000/static/blocks.png', - name: 'Custom Widget', - description: 'Add your own custom widget', - }, - ]; -} -export default ModularWidgets; diff --git a/src/components/views/elements/AppIconTile.js b/src/components/views/elements/AppIconTile.js deleted file mode 100644 index 32fcd74111..0000000000 --- a/src/components/views/elements/AppIconTile.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -'use strict'; -import React from 'react'; - -class AppIconTile extends React.Component { - constructor(props) { - super(props); - this._onTileClick = this._onTileClick.bind(this); - } - - _onTileClick(props) { - this.props.onClick(this.props.type); - } - - render() { - const contentClasses = ['mx_AppIconTile']; - // if(this.props.type == 'custom') { - // contentClasses.push('mx_AppIconTile_active'); - // } - - return ( -
-
- {this.props.name} -
-
-

{this.props.name}

-

{this.props.description}

-
-
- ); - } -} - -AppIconTile.propTypes = { - type: React.PropTypes.string.isRequired, - icon: React.PropTypes.string.isRequired, - name: React.PropTypes.string.isRequired, - description: React.PropTypes.string.isRequired, - onClick: React.PropTypes.func.isRequired, -}; - -export default AppIconTile; diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 3bf99dbddd..62463e5426 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -35,16 +35,6 @@ export default React.createClass({ }; }, - componentDidMount: function() { - console.log("App component %s mounted", this.props.id); - // setInterval(() => { - // const msg = "Message from riot"; - // const domain = 'http://localhost:8000'; - // this.refs.appFrame.contentWindow.postMessage(msg, domain); - // console.log("Sending message"); - // }, 3000); - }, - _onEditClick: function() { console.log("Edit widget %s", this.props.id); }, @@ -78,12 +68,12 @@ export default React.createClass({ {this.props.name} {/* Edit widget */} - Edit + /> */} {/* Delete widget */} - ); - - if (member.user) { - this.user_last_modified_time = member.user.getLastModifiedTime(); - } - this.member_last_modified_time = member.getLastModifiedTime(); - - return ( - - ); - } -}); diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 0047e2fddc..fbff785ba4 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -57,7 +57,7 @@ module.exports = React.createClass({ }, componentWillUnmount: function() { - ScalarMessaging.startListening(); + ScalarMessaging.stopListening(); if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); } From 18ea76b864ad641057884be0922a5a98343b9089 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 11:31:00 +0100 Subject: [PATCH 46/69] Removed commented code --- src/components/views/rooms/AppsDrawer.js | 66 ------------------------ 1 file changed, 66 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index fbff785ba4..ca56391cc3 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -135,72 +135,6 @@ module.exports = React.createClass({ }, onClickAddWidget: function(e) { - // Modal.createDialog(AddAppDialog, { - // onFinished: (proceed, type, value) => { - // if (!proceed || !type) return; - // if (type === 'custom' && !value) return; - // - // const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); - // let appsStateEvent = {}; - // if (appsStateEvents) { - // appsStateEvent = appsStateEvents.getContent(); - // } - // - // if (appsStateEvent[type]) { - // return; - // } - // - // switch (type) { - // case 'etherpad': - // appsStateEvent.etherpad = { - // type: type, - // url: 'http://localhost:8000/etherpad.html', - // }; - // break; - // case 'grafana': - // appsStateEvent.grafana = { - // type: type, - // url: 'http://localhost:8000/grafana.html', - // }; - // break; - // case 'jitsi': - // appsStateEvent.videoConf = { - // type: type, - // url: 'http://localhost:8000/jitsi.html', - // data: { - // confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), - // }, - // }; - // break; - // case 'vrdemo': - // appsStateEvent.vrDemo = { - // type: type, - // url: 'http://localhost:8000/vrdemo.html', - // data: { - // roomAlias: '#vrvc' + this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), - // }, - // }; - // break; - // case 'custom': - // appsStateEvent.custom = { - // type: type, - // url: value, - // }; - // break; - // default: - // console.warn('Unsupported app type:', type); - // return; - // } - // - // MatrixClientPeg.get().sendStateEvent( - // this.props.room.roomId, - // 'im.vector.modular.widgets', - // appsStateEvent, - // '', - // ); - // }, - // }); - if (e) { e.preventDefault(); } From 1f1352786f193ec5761f90f8bbd43ae7eb7816e7 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 11:38:14 +0100 Subject: [PATCH 47/69] Temporarily disable Jitsi default --- src/components/views/rooms/MessageComposer.js | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 8d9feccf7e..63c7f8c00a 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -152,41 +152,53 @@ export default class MessageComposer extends React.Component { }); } - _startCallApp(isAudioConf) { - dis.dispatch({ - action: 'appsDrawer', - show: true, - }); + // _startCallApp(isAudioConf) { + // dis.dispatch({ + // action: 'appsDrawer', + // show: true, + // }); - const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); - let appsStateEvent = {}; - if (appsStateEvents) { - appsStateEvent = appsStateEvents.getContent(); - } - if (!appsStateEvent.videoConf) { - appsStateEvent.videoConf = { - type: 'jitsi', - url: 'http://localhost:8000/jitsi.html', - data: { - confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), - isAudioConf: isAudioConf, - }, - }; - MatrixClientPeg.get().sendStateEvent( - this.props.room.roomId, - 'im.vector.modular.widgets', - appsStateEvent, - '', - ).then(() => console.log('Sent state'), (e) => console.error(e)); - } - } + // const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + // let appsStateEvent = {}; + // if (appsStateEvents) { + // appsStateEvent = appsStateEvents.getContent(); + // } + // if (!appsStateEvent.videoConf) { + // appsStateEvent.videoConf = { + // type: 'jitsi', + // url: 'http://localhost:8000/jitsi.html', + // data: { + // confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), + // isAudioConf: isAudioConf, + // }, + // }; + // MatrixClientPeg.get().sendStateEvent( + // this.props.room.roomId, + // 'im.vector.modular.widgets', + // appsStateEvent, + // '', + // ).then(() => console.log('Sent state'), (e) => console.error(e)); + // } + // } onCallClick(ev) { - this._startCallApp(false); + // NOTE -- Will be replaced by Jitsi code (currently commented) + dis.dispatch({ + action: 'place_call', + type: ev.shiftKey ? "screensharing" : "video", + room_id: this.props.room.roomId, + }); + // this._startCallApp(false); } onVoiceCallClick(ev) { - this._startCallApp(true); + // NOTE -- Will be replaced by Jitsi code (currently commented) + dis.dispatch({ + action: 'place_call', + type: "voice", + room_id: this.props.room.roomId, + }); + // this._startCallApp(true); } onShowAppsClick(ev) { From 63b12503859ddd7d07021821bab699246699052b Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 11:39:20 +0100 Subject: [PATCH 48/69] Add comment --- src/components/views/rooms/MessageComposer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 63c7f8c00a..eb7a00389f 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -166,6 +166,7 @@ export default class MessageComposer extends React.Component { // if (!appsStateEvent.videoConf) { // appsStateEvent.videoConf = { // type: 'jitsi', + // // FIXME -- This should not be localhost // url: 'http://localhost:8000/jitsi.html', // data: { // confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(), From 89f051e693393704ef23bc3d71129c7ec19591f1 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 11:52:49 +0100 Subject: [PATCH 49/69] Fix automerge error. --- src/components/structures/RoomView.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f2dd98d35c..bb50d7f78e 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -238,6 +238,7 @@ module.exports = React.createClass({ if (room) { this.setState({ unsentMessageError: this._getUnsentMessageError(room), + showApps: this._shouldShowApps(room), }); this._onRoomLoaded(room); } @@ -272,11 +273,6 @@ module.exports = React.createClass({ } else if (room) { // Stop peeking because we have joined this room previously MatrixClientPeg.get().stopPeeking(); - this.setState({ - showApps: this._shouldShowApps(room), - unsentMessageError: this._getUnsentMessageError(room), - }); - this._onRoomLoaded(room); } }, From ad9a3d9ddcc12860dc7f3280d44cf2df8efff5e9 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 11:55:32 +0100 Subject: [PATCH 50/69] Remove unused case statement. --- src/components/structures/RoomView.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index bb50d7f78e..d382702071 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -470,11 +470,6 @@ module.exports = React.createClass({ showApps: payload.show ? true : false, }); break; - case 'forward_event': - this.setState({ - forwardingEvent: payload.content, - }); - break; } }, From aab4c097e60c42010a6d4d8061806f7f4a2de75a Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 12:26:13 +0100 Subject: [PATCH 51/69] Make query parameters generic. --- src/components/views/rooms/AppsDrawer.js | 86 ++++++++++++++++-------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index ca56391cc3..8919fd6f74 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -63,32 +63,64 @@ module.exports = React.createClass({ } }, - _initAppConfig: function(appId, app) { - console.log("App props: ", this.props); - app.id = appId; - app.name = app.type; - - switch(app.type) { - case 'etherpad': - app.queryParams = '?userName=' + this.props.userId + - '&padId=' + this.props.room.roomId; - break; - case 'jitsi': { - const user = MatrixClientPeg.get().getUser(this.props.userId); - app.queryParams = '?confId=' + app.data.confId + - '&displayName=' + encodeURIComponent(user.displayName) + - '&avatarUrl=' + encodeURIComponent(MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl)) + - '&email=' + encodeURIComponent(this.props.userId) + - '&isAudioConf=' + app.data.isAudioConf; - - app.name += ' - ' + app.data.confId; - break; + /** + * Encodes a URI according to a set of template variables. Variables will be + * passed through encodeURIComponent. + * @param {string} pathTemplate The path with template variables e.g. '/foo/$bar'. + * @param {Object} variables The key/value pairs to replace the template + * variables with. E.g. { "$bar": "baz" }. + * @return {string} The result of replacing all template variables e.g. '/foo/baz'. + */ + encodeUri: function(pathTemplate, variables) { + for (const key in variables) { + if (!variables.hasOwnProperty(key)) { + continue; } - case 'vrdemo': - app.name = 'Matrix VR Demo - ' + app.data.roomAlias; - app.queryParams = '?roomAlias=' + encodeURIComponent(app.data.roomAlias); - break; + pathTemplate = pathTemplate.replace( + key, encodeURIComponent(variables[key]), + ); } + return pathTemplate; + }, + + _initAppConfig: function(appId, app) { + const user = MatrixClientPeg.get().getUser(this.props.userId); + const params = { + '$matrix_user_id': this.props.userId, + '$matrix_room_id': this.props.room.roomId, + '$matrix_display_name': user ? user.displayName : this.props.userId, + '$matrix_avatar_url': user ? MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl) : '', + }; + + if(app.data) { + Object.keys(app.data).forEach((key) => { + params['$' + key] = app.data[key]; + }); + } + + app.id = appId; + app.name = app.name || app.type; + app.url = this.encodeUri(app.url, params); + + // switch(app.type) { + // case 'etherpad': + // app.queryParams = '?userName=' + this.props.userId + + // '&padId=' + this.props.room.roomId; + // break; + // case 'jitsi': { + // + // app.queryParams = '?confId=' + app.data.confId + + // '&displayName=' + encodeURIComponent(user.displayName) + + // '&avatarUrl=' + encodeURIComponent(MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl)) + + // '&email=' + encodeURIComponent(this.props.userId) + + // '&isAudioConf=' + app.data.isAudioConf; + // + // break; + // } + // case 'vrdemo': + // app.queryParams = '?roomAlias=' + encodeURIComponent(app.data.roomAlias); + // break; + // } return app; }, @@ -156,14 +188,10 @@ module.exports = React.createClass({ render: function() { const apps = this.state.apps.map( (app, index, arr) => { - let appUrl = app.url; - if (app.queryParams) { - appUrl += app.queryParams; - } return Date: Tue, 27 Jun 2017 17:38:33 +0100 Subject: [PATCH 52/69] Add matrix apps to Labs settings --- src/UserSettingsStore.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 84d85e7565..a769c3627b 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -30,6 +30,11 @@ export default { id: 'rich_text_editor', default: false, }, + { + name: "Matrix Apps", + id: 'matrix_apps', + default: false, + }, ], // horrible but it works. The locality makes this somewhat more palatable. From 8dfd047f0364793d63b1f3827c49e90c5e69944b Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 17:39:29 +0100 Subject: [PATCH 53/69] Don't show widgets when editing room settings and lint fixes. --- src/components/structures/RoomView.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index d382702071..6e6325504a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -47,13 +47,12 @@ import UserProvider from '../../autocomplete/UserProvider'; import RoomViewStore from '../../stores/RoomViewStore'; -var DEBUG = false; +const DEBUG = false; +let debuglog = function() {}; if (DEBUG) { // using bind means that we get to keep useful line numbers in the console - var debuglog = console.log.bind(console); -} else { - var debuglog = function() {}; + debuglog = console.log.bind(console); } module.exports = React.createClass({ @@ -1623,7 +1622,7 @@ module.exports = React.createClass({ displayConfCallNotification={this.state.displayConfCallNotification} maxHeight={this.state.auxPanelMaxHeight} onResize={this.onChildResize} - showApps={this.state.showApps} > + showApps={this.state.showApps && !this.state.editingRoomSettings} > { aux } ); From 338a4db87f65b68a93548d38d263e74469867f30 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Tue, 27 Jun 2017 17:40:09 +0100 Subject: [PATCH 54/69] Only show apps drawer if matrix apps labs setting ids enabled --- src/components/views/rooms/AuxPanel.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index bc414d75c9..a50743a25d 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -20,7 +20,9 @@ import sdk from '../../../index'; import dis from "../../../dispatcher"; import ObjectUtils from '../../../ObjectUtils'; import AppsDrawer from './AppsDrawer'; -import { _t, _tJsx} from '../../../languageHandler'; +import { _t, _tJsx} from '../../../languageHandler'; +import UserSettingsStore from '../../../UserSettingsStore'; + module.exports = React.createClass({ displayName: 'AuxPanel', @@ -127,7 +129,7 @@ module.exports = React.createClass({ ); let appsDrawer = null; - if(this.props.showApps) { + if(UserSettingsStore.isFeatureEnabled('matrix_apps') && this.props.showApps) { appsDrawer = Date: Tue, 27 Jun 2017 17:40:28 +0100 Subject: [PATCH 55/69] Only show apps button if labs feature enabled. --- src/components/views/rooms/MessageComposer.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index eb7a00389f..7896a03376 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -315,16 +315,18 @@ export default class MessageComposer extends React.Component { } // Apps - if (this.props.showApps) { - hideAppsButton = -
- -
; - } else { - showAppsButton = -
- -
; + if (UserSettingsStore.isFeatureEnabled('matrix_apps')) { + if (this.props.showApps) { + hideAppsButton = +
+ +
; + } else { + showAppsButton = +
+ +
; + } } const canSendMessages = this.props.room.currentState.maySendMessage( From ad2517bd8bbd42e1a1df5484ba70f5c769952e53 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 10:27:06 +0100 Subject: [PATCH 56/69] Uppercase the first letter of the app tile name. --- src/components/views/elements/AppTile.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 62463e5426..dc34d15d7f 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -61,11 +61,20 @@ export default React.createClass({ } }, + formatAppTileName: function() { + let appTileName = "No name"; + if(this.props.name && this.props.name.trim()) { + appTileName = this.props.name.trim(); + appTileName = appTileName[0].toUpperCase() + appTileName.slice(1).toLowerCase(); + } + return appTileName; + }, + render: function() { return (
- {this.props.name} + {this.formatAppTileName()} {/* Edit widget */} {/* Date: Wed, 28 Jun 2017 12:00:22 +0100 Subject: [PATCH 57/69] Add translation for Matrix Apps labs setting. --- src/UserSettingsStore.js | 3 +- src/i18n/strings/en_EN.json | 1855 ++++++++++++++++++----------------- src/i18n/strings/en_US.json | 1 + 3 files changed, 931 insertions(+), 928 deletions(-) diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index a769c3627b..009fdabb53 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -31,7 +31,7 @@ export default { default: false, }, { - name: "Matrix Apps", + name: "-", id: 'matrix_apps', default: false, }, @@ -40,6 +40,7 @@ export default { // horrible but it works. The locality makes this somewhat more palatable. doTranslations: function() { this.LABS_FEATURES[0].name = _t("New Composer & Autocomplete"); + this.LABS_FEATURES[1].name = _t("Matrix Apps"); }, loadProfileInfo: function() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0c823229d5..b8f5c26332 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1,929 +1,930 @@ { - "af":"Afrikaans", - "ar-ae":"Arabic (U.A.E.)", - "ar-bh":"Arabic (Bahrain)", - "ar-dz":"Arabic (Algeria)", - "ar-eg":"Arabic (Egypt)", - "ar-iq":"Arabic (Iraq)", - "ar-jo":"Arabic (Jordan)", - "ar-kw":"Arabic (Kuwait)", - "ar-lb":"Arabic (Lebanon)", - "ar-ly":"Arabic (Libya)", - "ar-ma":"Arabic (Morocco)", - "ar-om":"Arabic (Oman)", - "ar-qa":"Arabic (Qatar)", - "ar-sa":"Arabic (Saudi Arabia)", - "ar-sy":"Arabic (Syria)", - "ar-tn":"Arabic (Tunisia)", - "ar-ye":"Arabic (Yemen)", - "be":"Belarusian", - "bg":"Bulgarian", - "ca":"Catalan", - "cs":"Czech", - "da":"Danish", - "de-at":"German (Austria)", - "de-ch":"German (Switzerland)", - "de":"German", - "de-li":"German (Liechtenstein)", - "de-lu":"German (Luxembourg)", - "el":"Greek", - "en-au":"English (Australia)", - "en-bz":"English (Belize)", - "en-ca":"English (Canada)", - "en":"English", - "en-gb":"English (United Kingdom)", - "en-ie":"English (Ireland)", - "en-jm":"English (Jamaica)", - "en-nz":"English (New Zealand)", - "en-tt":"English (Trinidad)", - "en-us":"English (United States)", - "en-za":"English (South Africa)", - "es-ar":"Spanish (Argentina)", - "es-bo":"Spanish (Bolivia)", - "es-cl":"Spanish (Chile)", - "es-co":"Spanish (Colombia)", - "es-cr":"Spanish (Costa Rica)", - "es-do":"Spanish (Dominican Republic)", - "es-ec":"Spanish (Ecuador)", - "es-gt":"Spanish (Guatemala)", - "es-hn":"Spanish (Honduras)", - "es-mx":"Spanish (Mexico)", - "es-ni":"Spanish (Nicaragua)", - "es-pa":"Spanish (Panama)", - "es-pe":"Spanish (Peru)", - "es-pr":"Spanish (Puerto Rico)", - "es-py":"Spanish (Paraguay)", - "es":"Spanish (Spain)", - "es-sv":"Spanish (El Salvador)", - "es-uy":"Spanish (Uruguay)", - "es-ve":"Spanish (Venezuela)", - "et":"Estonian", - "eu":"Basque (Basque)", - "fa":"Farsi", - "fi":"Finnish", - "fo":"Faeroese", - "fr-be":"French (Belgium)", - "fr-ca":"French (Canada)", - "fr-ch":"French (Switzerland)", - "fr":"French", - "fr-lu":"French (Luxembourg)", - "ga":"Irish", - "gd":"Gaelic (Scotland)", - "he":"Hebrew", - "hi":"Hindi", - "hr":"Croatian", - "hu":"Hungarian", - "id":"Indonesian", - "is":"Icelandic", - "it-ch":"Italian (Switzerland)", - "it":"Italian", - "ja":"Japanese", - "ji":"Yiddish", - "ko":"Korean", - "lt":"Lithuanian", - "lv":"Latvian", - "mk":"Macedonian (FYROM)", - "ms":"Malaysian", - "mt":"Maltese", - "nl-be":"Dutch (Belgium)", - "nl":"Dutch", - "no":"Norwegian", - "pl":"Polish", - "pt-br":"Brazilian Portuguese", - "pt":"Portuguese", - "rm":"Rhaeto-Romanic", - "ro-mo":"Romanian (Republic of Moldova)", - "ro":"Romanian", - "ru-mo":"Russian (Republic of Moldova)", - "ru":"Russian", - "sb":"Sorbian", - "sk":"Slovak", - "sl":"Slovenian", - "sq":"Albanian", - "sr":"Serbian", - "sv-fi":"Swedish (Finland)", - "sv":"Swedish", - "sx":"Sutu", - "sz":"Sami (Lappish)", - "th":"Thai", - "tn":"Tswana", - "tr":"Turkish", - "ts":"Tsonga", - "uk":"Ukrainian", - "ur":"Urdu", - "ve":"Venda", - "vi":"Vietnamese", - "xh":"Xhosa", - "zh-cn":"Chinese (PRC)", - "zh-hk":"Chinese (Hong Kong SAR)", - "zh-sg":"Chinese (Singapore)", - "zh-tw":"Chinese (Taiwan)", - "zu":"Zulu", - "a room": "a room", - "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", - "Accept": "Accept", - "%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.", - "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", - "Account": "Account", - "Access Token:": "Access Token:", - "Active call (%(roomName)s)": "Active call (%(roomName)s)", - "Add": "Add", - "Add a topic": "Add a topic", - "Add email address": "Add email address", - "Add phone number": "Add phone number", - "Admin": "Admin", - "Admin tools": "Admin tools", - "And %(count)s more...": "And %(count)s more...", - "VoIP": "VoIP", - "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", - "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", - "Default Device": "Default Device", - "Microphone": "Microphone", - "Camera": "Camera", - "Advanced": "Advanced", - "Algorithm": "Algorithm", - "Hide removed messages": "Hide removed messages", - "Always show message timestamps": "Always show message timestamps", - "Authentication": "Authentication", - "Alias (optional)": "Alias (optional)", - "all room members": "all room members", - "all room members, from the point they are invited": "all room members, from the point they are invited", - "all room members, from the point they joined": "all room members, from the point they joined", - "and": "and", - "%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others", - "%(items)s and one other": "%(items)s and one other", - "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", - "and %(overflowCount)s others...": "and %(overflowCount)s others...", - "and one other...": "and one other...", - "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", - "%(names)s and one other are typing": "%(names)s and one other are typing", - "%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing", - "An email has been sent to": "An email has been sent to", - "A new password must be entered.": "A new password must be entered.", - "%(senderName)s answered the call.": "%(senderName)s answered the call.", - "anyone": "anyone", - "An error has occurred.": "An error has occurred.", - "Anyone": "Anyone", - "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests", - "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests", - "Are you sure?": "Are you sure?", - "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", - "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", - "Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?", - "Attachment": "Attachment", - "Autoplay GIFs and videos": "Autoplay GIFs and videos", - "%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.", - "Ban": "Ban", - "Banned users": "Banned users", - "Bans user with given id": "Bans user with given id", - "Blacklisted": "Blacklisted", - "Bug Report": "Bug Report", - "Bulk Options": "Bulk Options", - "Call Timeout": "Call Timeout", - "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", - "Can't load user settings": "Can't load user settings", - "Change Password": "Change Password", - "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.", - "%(senderName)s changed their profile picture.": "%(senderName)s changed their profile picture.", - "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", - "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s changed the room name to %(roomName)s.", - "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s removed the room name.", - "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".", - "Changes to who can read history will only apply to future messages in this room": "Changes to who can read history will only apply to future messages in this room", - "Changes your display nickname": "Changes your display nickname", - "changing room on a RoomView is not supported": "changing room on a RoomView is not supported", - "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.": "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.", - "Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key", - "Clear Cache and Reload": "Clear Cache and Reload", - "Clear Cache": "Clear Cache", - "Click here to join the discussion!": "Click here to join the discussion!", - "Click here to fix": "Click here to fix", - "Click to mute audio": "Click to mute audio", - "Click to mute video": "Click to mute video", - "click to reveal": "click to reveal", - "Click to unmute video": "Click to unmute video", - "Click to unmute audio": "Click to unmute audio", - "Close": "Close", - "Command error": "Command error", - "Commands": "Commands", - "Conference call failed.": "Conference call failed.", - "Conference calling is in development and may not be reliable.": "Conference calling is in development and may not be reliable.", - "Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms", - "Conference calls are not supported in this client": "Conference calls are not supported in this client", - "Confirm password": "Confirm password", - "Confirm your new password": "Confirm your new password", - "Continue": "Continue", - "Could not connect to the integration server": "Could not connect to the integration server", - "%(count)s new messages": { - "one": "%(count)s new message", - "other": "%(count)s new messages" - }, - "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", - "Create an account": "Create an account", - "Create Room": "Create Room", - "Cryptography": "Cryptography", - "Current password": "Current password", - "Curve25519 identity key": "Curve25519 identity key", - "Custom": "Custom", - "Custom level": "Custom level", - "/ddg is not a command": "/ddg is not a command", - "Deactivate Account": "Deactivate Account", - "Deactivate my account": "Deactivate my account", - "Decline": "Decline", - "Decrypt %(text)s": "Decrypt %(text)s", - "Decryption error": "Decryption error", - "Delete": "Delete", - "demote": "demote", - "Deops user with given id": "Deops user with given id", - "Default": "Default", - "Define the power level of a user": "Define the power level of a user", - "Device already verified!": "Device already verified!", - "Device ID": "Device ID", - "Device ID:": "Device ID:", - "device id: ": "device id: ", - "Device key:": "Device key:", - "Devices": "Devices", - "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room", - "Direct Chat": "Direct Chat", - "Direct chats": "Direct chats", - "Disable Notifications": "Disable Notifications", - "disabled": "disabled", - "Disable inline URL previews by default": "Disable inline URL previews by default", - "Disable markdown formatting": "Disable markdown formatting", - "Disinvite": "Disinvite", - "Display name": "Display name", - "Displays action": "Displays action", - "Don't send typing notifications": "Don't send typing notifications", - "Download %(text)s": "Download %(text)s", - "Drop File Here": "Drop File Here", - "Drop here %(toAction)s": "Drop here %(toAction)s", - "Drop here to tag %(section)s": "Drop here to tag %(section)s", - "Ed25519 fingerprint": "Ed25519 fingerprint", - "Email": "Email", - "Email address": "Email address", - "Email address (optional)": "Email address (optional)", - "Email, name or matrix ID": "Email, name or matrix ID", - "Emoji": "Emoji", - "Enable automatic language detection for syntax highlighting": "Enable automatic language detection for syntax highlighting", - "Enable encryption": "Enable encryption", - "Enable Notifications": "Enable Notifications", - "enabled": "enabled", - "Encrypted by a verified device": "Encrypted by a verified device", - "Encrypted by an unverified device": "Encrypted by an unverified device", - "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption", - "Encrypted room": "Encrypted room", - "Encryption is enabled in this room": "Encryption is enabled in this room", - "Encryption is not enabled in this room": "Encryption is not enabled in this room", - "%(senderName)s ended the call.": "%(senderName)s ended the call.", - "End-to-end encryption information": "End-to-end encryption information", - "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable", - "Enter Code": "Enter Code", - "Enter passphrase": "Enter passphrase", - "Error": "Error", - "Error decrypting attachment": "Error decrypting attachment", - "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", - "Event information": "Event information", - "Existing Call": "Existing Call", - "Export": "Export", - "Export E2E room keys": "Export E2E room keys", - "Failed to ban user": "Failed to ban user", - "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", - "Failed to change power level": "Failed to change power level", - "Failed to delete device": "Failed to delete device", - "Failed to fetch avatar URL": "Failed to fetch avatar URL", - "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", - "Failed to join room": "Failed to join room", - "Failed to join the room": "Failed to join the room", - "Failed to kick": "Failed to kick", - "Failed to leave room": "Failed to leave room", - "Failed to load timeline position": "Failed to load timeline position", - "Failed to lookup current room": "Failed to lookup current room", - "Failed to mute user": "Failed to mute user", - "Failed to register as guest:": "Failed to register as guest:", - "Failed to reject invite": "Failed to reject invite", - "Failed to reject invitation": "Failed to reject invitation", - "Failed to save settings": "Failed to save settings", - "Failed to send email": "Failed to send email", - "Failed to send request.": "Failed to send request.", - "Failed to set avatar.": "Failed to set avatar.", - "Failed to set display name": "Failed to set display name", - "Failed to set up conference call": "Failed to set up conference call", - "Failed to toggle moderator status": "Failed to toggle moderator status", - "Failed to unban": "Failed to unban", - "Failed to upload file": "Failed to upload file", - "Failed to upload profile picture!": "Failed to upload profile picture!", - "Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email", - "Failure to create room": "Failure to create room", - "Favourite": "Favourite", - "favourite": "favourite", - "Favourites": "Favourites", - "Fill screen": "Fill screen", - "Filter room members": "Filter room members", - "Forget room": "Forget room", - "Forgot your password?": "Forgot your password?", - "For security, this session has been signed out. Please sign in again.": "For security, this session has been signed out. Please sign in again.", - "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.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.", - "Found a bug?": "Found a bug?", - "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", - "Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.", - "Guests can't set avatars. Please register.": "Guests can't set avatars. Please register.", - "Guest users can't create new rooms. Please register to create room and start a chat.": "Guest users can't create new rooms. Please register to create room and start a chat.", - "Guest users can't upload files. Please register to upload.": "Guest users can't upload files. Please register to upload.", - "Guests can't use labs features. Please register.": "Guests can't use labs features. Please register.", - "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", - "had": "had", - "Hangup": "Hangup", - "Hide read receipts": "Hide read receipts", - "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", - "Historical": "Historical", - "Home": "Home", - "Homeserver is": "Homeserver is", - "Identity Server is": "Identity Server is", - "I have verified my email address": "I have verified my email address", - "Import": "Import", - "Import E2E room keys": "Import E2E room keys", - "Incoming call from %(name)s": "Incoming call from %(name)s", - "Incoming video call from %(name)s": "Incoming video call from %(name)s", - "Incoming voice call from %(name)s": "Incoming voice call from %(name)s", - "Incorrect username and/or password.": "Incorrect username and/or password.", - "Incorrect verification code": "Incorrect verification code", - "Interface Language": "Interface Language", - "Invalid alias format": "Invalid alias format", - "Invalid address format": "Invalid address format", - "Invalid Email Address": "Invalid Email Address", - "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", - "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", - "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", - "%(displayName)s is typing": "%(displayName)s is typing", - "Sign in with": "Sign in with", - "Join as voice or video.": "Join as voice or video.", - "Join Room": "Join Room", - "joined and left": "joined and left", - "joined": "joined", - "%(targetName)s joined the room.": "%(targetName)s joined the room.", - "Joins room with given alias": "Joins room with given alias", - "Jump to first unread message.": "Jump to first unread message.", - "%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.", - "Kick": "Kick", - "Kicks user with given id": "Kicks user with given id", - "Labs": "Labs", - "Last seen": "Last seen", - "Leave room": "Leave room", - "left and rejoined": "left and rejoined", - "left": "left", - "%(targetName)s left the room.": "%(targetName)s left the room.", - "Level:": "Level:", - "List this room in %(domain)s's room directory?": "List this room in %(domain)s's room directory?", - "Local addresses for this room:": "Local addresses for this room:", - "Logged in as:": "Logged in as:", - "Login as guest": "Login as guest", - "Logout": "Logout", - "Low priority": "Low priority", - "%(senderName)s made future room history visible to": "%(senderName)s made future room history visible to", - "Manage Integrations": "Manage Integrations", - "Markdown is disabled": "Markdown is disabled", - "Markdown is enabled": "Markdown is enabled", - "matrix-react-sdk version:": "matrix-react-sdk version:", - "Members only": "Members only", - "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", - "Missing room_id in request": "Missing room_id in request", - "Missing user_id in request": "Missing user_id in request", - "Mobile phone number": "Mobile phone number", - "Mobile phone number (optional)": "Mobile phone number (optional)", - "Moderator": "Moderator", - "Must be viewing a room": "Must be viewing a room", - "Mute": "Mute", - "my Matrix ID": "my Matrix ID", - "Name": "Name", - "Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device", - "Never send encrypted messages to unverified devices in this room": "Never send encrypted messages to unverified devices in this room", - "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", - "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", - "New Composer & Autocomplete": "New Composer & Autocomplete", - "New password": "New password", - "New passwords don't match": "New passwords don't match", - "New passwords must match each other.": "New passwords must match each other.", - "none": "none", - "not set": "not set", - "not specified": "not specified", - "Notifications": "Notifications", - "(not supported by this browser)": "(not supported by this browser)", - "": "", - "NOT verified": "NOT verified", - "No devices with registered encryption keys": "No devices with registered encryption keys", - "No display name": "No display name", - "No more results": "No more results", - "No results": "No results", - "No users have specific privileges in this room": "No users have specific privileges in this room", - "OK": "OK", - "olm version:": "olm version:", - "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)", - "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", - "Only people who have been invited": "Only people who have been invited", - "Operation failed": "Operation failed", - "Otherwise, click here to send a bug report.": "Otherwise, click here to send a bug report.", - "Password": "Password", - "Password:": "Password:", - "Passwords can't be empty": "Passwords can't be empty", - "People": "People", - "Permissions": "Permissions", - "Phone": "Phone", - "%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.", - "Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.", - "Please Register": "Please Register", - "Power level must be positive integer.": "Power level must be positive integer.", - "Press": "Press", - "Press to start a chat with someone": "Press to start a chat with someone", - "Privacy warning": "Privacy warning", - "Private Chat": "Private Chat", - "Privileged Users": "Privileged Users", - "Profile": "Profile", - "Public Chat": "Public Chat", - "Reason": "Reason", - "Reason: %(reasonText)s": "Reason: %(reasonText)s", - "Revoke Moderator": "Revoke Moderator", - "Refer a friend to Riot:": "Refer a friend to Riot:", - "Register": "Register", - "rejected": "rejected", - "%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.", - "Reject invitation": "Reject invitation", - "Rejoin": "Rejoin", - "Remote addresses for this room:": "Remote addresses for this room:", - "Remove Contact Information?": "Remove Contact Information?", - "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).", - "%(senderName)s removed their profile picture.": "%(senderName)s removed their profile picture.", - "Remove": "Remove", - "Remove %(threePid)s?": "Remove %(threePid)s?", - "%(senderName)s requested a VoIP conference.": "%(senderName)s requested a VoIP conference.", - "Report it": "Report it", - "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", - "restore": "restore", - "Results from DuckDuckGo": "Results from DuckDuckGo", - "Return to app": "Return to app", - "Return to login screen": "Return to login screen", - "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings", - "Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again", - "riot-web version:": "riot-web version:", - "Room %(roomId)s not visible": "Room %(roomId)s not visible", - "Room Colour": "Room Colour", - "Room contains unknown devices": "Room contains unknown devices", - "Room name (optional)": "Room name (optional)", - "%(roomName)s does not exist.": "%(roomName)s does not exist.", - "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", - "Rooms": "Rooms", - "Save": "Save", - "Scroll to bottom of page": "Scroll to bottom of page", - "Scroll to unread messages": "Scroll to unread messages", - "Search": "Search", - "Search failed": "Search failed", - "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", - "Searching known users": "Searching known users", - "Seen by %(userName)s at %(dateTime)s": "Seen by %(userName)s at %(dateTime)s", - "Send a message (unencrypted)": "Send a message (unencrypted)", - "Send an encrypted message": "Send an encrypted message", - "Send anyway": "Send anyway", - "Sender device information": "Sender device information", - "Send Invites": "Send Invites", - "Send Reset Email": "Send Reset Email", - "sent an image": "sent an image", - "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s sent an image.", - "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.", - "sent a video": "sent a video", - "Server error": "Server error", - "Server may be unavailable or overloaded": "Server may be unavailable or overloaded", - "Server may be unavailable, overloaded, or search timed out :(": "Server may be unavailable, overloaded, or search timed out :(", - "Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big", - "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", - "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", - "Session ID": "Session ID", - "%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.", - "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", - "Set": "Set", - "Settings": "Settings", - "Show panel": "Show panel", - "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", - "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", - "Signed Out": "Signed Out", - "Sign in": "Sign in", - "Sign out": "Sign out", - "since the point in time of selecting this option": "since the point in time of selecting this option", - "since they joined": "since they joined", - "since they were invited": "since they were invited", - "Some of your messages have not been sent.": "Some of your messages have not been sent.", - "Someone": "Someone", - "Sorry, this homeserver is using a login which is not recognised ": "Sorry, this homeserver is using a login which is not recognised ", - "Start a chat": "Start a chat", - "Start authentication": "Start authentication", - "Start Chat": "Start Chat", - "Submit": "Submit", - "Success": "Success", - "tag as %(tagName)s": "tag as %(tagName)s", - "tag direct chat": "tag direct chat", - "Tagged as: ": "Tagged as: ", - "The default role for new room members is": "The default role for new room members is", - "The main address for this room is": "The main address for this room is", - "The phone number entered looks invalid": "The phone number entered looks invalid", - "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", - "This action cannot be performed by a guest user. Please register to be able to do this.": "This action cannot be performed by a guest user. Please register to be able to do this.", - "This email address is already in use": "This email address is already in use", - "This email address was not found": "This email address was not found", - "%(actionVerb)s this person?": "%(actionVerb)s this person?", - "The email address linked to your account must be entered.": "The email address linked to your account must be entered.", - "The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads", - "The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload", - "The remote side failed to pick up": "The remote side failed to pick up", - "This Home Server does not support login using email address.": "This Home Server does not support login using email address.", - "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", - "There was a problem logging in.": "There was a problem logging in.", - "This room has no local addresses": "This room has no local addresses", - "This room is not recognised.": "This room is not recognised.", - "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", - "The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged", - "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address", - "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", - "This phone number is already in use": "This phone number is already in use", - "This room": "This room", - "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", - "This room's internal ID is": "This room's internal ID is", - "times": "times", - "To ban users": "To ban users", - "to browse the directory": "to browse the directory", - "To configure the room": "To configure the room", - "to demote": "to demote", - "to favourite": "to favourite", - "To invite users into the room": "To invite users into the room", - "To kick users": "To kick users", - "To link to a room it must have an address.": "To link to a room it must have an address.", - "to make a room or": "to make a room or", - "To remove other users' messages": "To remove other users' messages", - "To reset your password, enter the email address linked to your account": "To reset your password, enter the email address linked to your account", - "to restore": "to restore", - "To send events of type": "To send events of type", - "To send messages": "To send messages", - "to start a chat with someone": "to start a chat with someone", - "to tag as %(tagName)s": "to tag as %(tagName)s", - "to tag direct chat": "to tag direct chat", - "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", - "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", - "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", - "Turn Markdown off": "Turn Markdown off", - "Turn Markdown on": "Turn Markdown on", - "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).", - "Unable to add email address": "Unable to add email address", - "Unable to create widget.": "Unable to create widget.", - "Unable to remove contact information": "Unable to remove contact information", - "Unable to restore previous session": "Unable to restore previous session", - "Unable to verify email address.": "Unable to verify email address.", - "Unban": "Unban", - "Unbans user with given id": "Unbans user with given id", - "%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.", - "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", - "Unable to capture screen": "Unable to capture screen", - "Unable to enable Notifications": "Unable to enable Notifications", - "Unable to load device list": "Unable to load device list", - "Undecryptable": "Undecryptable", - "Unencrypted room": "Unencrypted room", - "unencrypted": "unencrypted", - "Unencrypted message": "Unencrypted message", - "unknown caller": "unknown caller", - "Unknown command": "Unknown command", - "unknown device": "unknown device", - "unknown error code": "unknown error code", - "Unknown room %(roomId)s": "Unknown room %(roomId)s", - "Unknown (user, device) pair:": "Unknown (user, device) pair:", - "unknown": "unknown", - "Unmute": "Unmute", - "Unnamed Room": "Unnamed Room", - "Unrecognised command:": "Unrecognised command:", - "Unrecognised room alias:": "Unrecognised room alias:", - "Unverified": "Unverified", - "Uploading %(filename)s and %(count)s others": { - "zero": "Uploading %(filename)s", - "one": "Uploading %(filename)s and %(count)s other", - "other": "Uploading %(filename)s and %(count)s others" - }, - "uploaded a file": "uploaded a file", - "Upload avatar": "Upload avatar", - "Upload Failed": "Upload Failed", - "Upload Files": "Upload Files", - "Upload file": "Upload file", - "Upload new:": "Upload new:", - "Usage": "Usage", - "Use compact timeline layout": "Use compact timeline layout", - "Use with caution": "Use with caution", - "User ID": "User ID", - "User Interface": "User Interface", - "%(user)s is a": "%(user)s is a", - "User name": "User name", - "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)", - "Username invalid: %(errMessage)s": "Username invalid: %(errMessage)s", - "Users": "Users", - "User": "User", - "Verification Pending": "Verification Pending", - "Verification": "Verification", - "verified": "verified", - "Verified": "Verified", - "Verified key": "Verified key", - "Video call": "Video call", - "Voice call": "Voice call", - "VoIP conference finished.": "VoIP conference finished.", - "VoIP conference started.": "VoIP conference started.", - "VoIP is unsupported": "VoIP is unsupported", - "(could not connect media)": "(could not connect media)", - "(no answer)": "(no answer)", - "(unknown failure: %(reason)s)": "(unknown failure: %(reason)s)", - "(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)", - "Warning!": "Warning!", - "WARNING: Device already verified, but keys do NOT MATCH!": "WARNING: Device already verified, but keys do NOT MATCH!", - "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "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!", - "Who can access this room?": "Who can access this room?", - "Who can read history?": "Who can read history?", - "Who would you like to add to this room?": "Who would you like to add to this room?", - "Who would you like to communicate with?": "Who would you like to communicate with?", - "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", - "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", - "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", - "You are already in a call.": "You are already in a call.", - "You're not in any rooms yet! Press to make a room or to browse the directory": "You're not in any rooms yet! Press to make a room or to browse the directory", - "You are trying to access %(roomName)s.": "You are trying to access %(roomName)s.", - "You cannot place a call with yourself.": "You cannot place a call with yourself.", - "You cannot place VoIP calls in this browser.": "You cannot place VoIP calls in this browser.", - "You do not have permission to post to this room": "You do not have permission to post to this room", - "You have been banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", - "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s", - "You have been kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)s.", - "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device", - "You have disabled URL previews by default.": "You have disabled URL previews by default.", - "You have enabled URL previews by default.": "You have enabled URL previews by default.", - "You have entered an invalid contact. Try using their Matrix ID or email address.": "You have entered an invalid contact. Try using their Matrix ID or email address.", - "You have no visible notifications": "You have no visible notifications", - "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", - "you must be a": "you must be a", - "You must register to use this functionality": "You must register to use this functionality", - "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", - "You need to be logged in.": "You need to be logged in.", - "You need to enter a user name.": "You need to enter a user name.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", - "Your password has been reset": "Your password has been reset", - "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them", - "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?", - "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?", - "You should not yet trust it to secure data": "You should not yet trust it to secure data", - "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.", - "Your home server does not support device management.": "Your home server does not support device management.", - "Sun": "Sun", - "Mon": "Mon", - "Tue": "Tue", - "Wed": "Wed", - "Thu": "Thu", - "Fri": "Fri", - "Sat": "Sat", - "Jan": "Jan", - "Feb": "Feb", - "Mar": "Mar", - "Apr": "Apr", - "May": "May", - "Jun": "Jun", - "Jul": "Jul", - "Aug": "Aug", - "Sep": "Sep", - "Oct": "Oct", - "Nov": "Nov", - "Dec": "Dec", - "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s", - "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", - "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", - "Set a display name:": "Set a display name:", - "Set a Display Name": "Set a Display Name", - "Upload an avatar:": "Upload an avatar:", - "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", - "Missing password.": "Missing password.", - "Passwords don't match.": "Passwords don't match.", - "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Password too short (min %(MIN_PASSWORD_LENGTH)s).", - "This doesn't look like a valid email address.": "This doesn't look like a valid email address.", - "This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.", - "User names may only contain letters, numbers, dots, hyphens and underscores.": "User names may only contain letters, numbers, dots, hyphens and underscores.", - "An unknown error occurred.": "An unknown error occurred.", - "I already have an account": "I already have an account", - "An error occurred: %(error_string)s": "An error occurred: %(error_string)s", - "Topic": "Topic", - "Make Moderator": "Make Moderator", - "Make this room private": "Make this room private", - "Share message history with new users": "Share message history with new users", - "Encrypt room": "Encrypt room", - "There are no visible files in this room": "There are no visible files in this room", - "Room": "Room", - "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", - "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.", - "Auto-complete": "Auto-complete", - "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Resend all or cancel all now. You can also select individual messages to resend or cancel.", - "(~%(count)s results)": { - "one": "(~%(count)s result)", - "other": "(~%(count)s results)" - }, - "Cancel": "Cancel", - "or": "or", - "Active call": "Active call", - "Monday": "Monday", - "Tuesday": "Tuesday", - "Wednesday": "Wednesday", - "Thursday": "Thursday", - "Friday": "Friday", - "Saturday": "Saturday", - "Sunday": "Sunday", - "bold": "bold", - "italic": "italic", - "strike": "strike", - "underline": "underline", - "code":"code", - "quote":"quote", - "bullet":"bullet", - "numbullet":"numbullet", - "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times", - "%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times", - "%(severalUsers)sjoined": "%(severalUsers)sjoined", - "%(oneUser)sjoined": "%(oneUser)sjoined", - "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times", - "%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times", - "%(severalUsers)sleft": "%(severalUsers)sleft", - "%(oneUser)sleft": "%(oneUser)sleft", - "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times", - "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times", - "%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left", - "%(oneUser)sjoined and left": "%(oneUser)sjoined and left", - "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times", - "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times", - "%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined", - "%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined", - "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times", - "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times", - "%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations", - "%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation", - "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times", - "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times", - "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn", - "%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn", - "were invited %(repeats)s times": "were invited %(repeats)s times", - "was invited %(repeats)s times": "was invited %(repeats)s times", - "were invited": "were invited", - "was invited": "was invited", - "were banned %(repeats)s times": "were banned %(repeats)s times", - "was banned %(repeats)s times": "was banned %(repeats)s times", - "were banned": "were banned", - "was banned": "was banned", - "were unbanned %(repeats)s times": "were unbanned %(repeats)s times", - "was unbanned %(repeats)s times": "was unbanned %(repeats)s times", - "were unbanned": "were unbanned", - "was unbanned": "was unbanned", - "were kicked %(repeats)s times": "were kicked %(repeats)s times", - "was kicked %(repeats)s times": "was kicked %(repeats)s times", - "were kicked": "were kicked", - "was kicked": "was kicked", - "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times", - "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times", - "%(severalUsers)schanged their name": "%(severalUsers)schanged their name", - "%(oneUser)schanged their name": "%(oneUser)schanged their name", - "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times", - "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times", - "%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar", - "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar", - "Please select the destination room for this message": "Please select the destination room for this message", - "Create new room": "Create new room", - "Welcome page": "Welcome page", - "Room directory": "Room directory", - "Start chat": "Start chat", - "New Password": "New Password", - "Start automatically after system login": "Start automatically after system login", - "Desktop specific": "Desktop specific", - "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.", - "Passphrases must match": "Passphrases must match", - "Passphrase must not be empty": "Passphrase must not be empty", - "Export room keys": "Export room keys", - "Confirm passphrase": "Confirm passphrase", - "Import room keys": "Import room keys", - "File to import": "File to import", - "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "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", - "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.", - "Failed to invite": "Failed to invite", - "Failed to invite user": "Failed to invite user", - "Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:", - "Confirm Removal": "Confirm Removal", - "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", - "Unknown error": "Unknown error", - "Incorrect password": "Incorrect password", - "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "This will make your account permanently unusable. You will not be able to re-register the same user ID.", - "This action is irreversible.": "This action is irreversible.", - "To continue, please enter your password.": "To continue, please enter your password.", - "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:", - "Device name": "Device name", - "Device Name": "Device Name", - "Device key": "Device key", - "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.": "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.", - "In future this verification process will be more sophisticated.": "In future this verification process will be more sophisticated.", - "Verify device": "Verify device", - "I verify that the keys match": "I verify that the keys match", - "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.", - "Unable to restore session": "Unable to restore session", - "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "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.", - "Continue anyway": "Continue anyway", - "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?", - "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.", - "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.", - "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", - "Unknown devices": "Unknown devices", - "Unknown Address": "Unknown Address", - "Unblacklist": "Unblacklist", - "Blacklist": "Blacklist", - "Unverify": "Unverify", - "Verify...": "Verify...", - "ex. @bob:example.com": "ex. @bob:example.com", - "Add User": "Add User", - "This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot", - "Sign in with CAS": "Sign in with CAS", - "Custom Server Options": "Custom Server Options", - "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.", - "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.", - "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.", - "Dismiss": "Dismiss", - "Please check your email to continue registration.": "Please check your email to continue registration.", - "Token incorrect": "Token incorrect", - "A text message has been sent to": "A text message has been sent to", - "Please enter the code it contains:": "Please enter the code it contains:", - "powered by Matrix": "powered by Matrix", - "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", - "You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s", - "Default server": "Default server", - "Custom server": "Custom server", - "Home server URL": "Home server URL", - "Identity server URL": "Identity server URL", - "What does this mean?": "What does this mean?", - "Error decrypting audio": "Error decrypting audio", - "Error decrypting image": "Error decrypting image", - "Image '%(Body)s' cannot be displayed.": "Image '%(Body)s' cannot be displayed.", - "This image cannot be displayed.": "This image cannot be displayed.", - "Error decrypting video": "Error decrypting video", - "Add an Integration": "Add an Integration", - "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "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?", - "Removed or unknown message type": "Removed or unknown message type", - "Disable URL previews by default for participants in this room": "Disable URL previews by default for participants in this room", - "Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)", - "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", - "URL Previews": "URL Previews", - "Enable URL previews for this room (affects only you)": "Enable URL previews for this room (affects only you)", - "Drop file here to upload": "Drop file here to upload", - " (unsupported)": " (unsupported)", - "Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)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", - "Updates": "Updates", - "Check for update": "Check for update", - "Start chatting": "Start chatting", - "Start Chatting": "Start Chatting", - "Click on the button below to start chatting!": "Click on the button below to start chatting!", - "$senderDisplayName changed the room avatar to ": "$senderDisplayName changed the room avatar to ", - "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.", - "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s", - "Username available": "Username available", - "Username not available": "Username not available", - "Something went wrong!": "Something went wrong!", - "This will be your account name on the homeserver, or you can pick a different server.": "This will be your account name on the homeserver, or you can pick a different server.", - "If you already have a Matrix account you can log in instead.": "If you already have a Matrix account you can log in instead.", - "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", - "Not a valid Riot keyfile": "Not a valid Riot keyfile", - "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", - "Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls", - "Do you want to set an email address?": "Do you want to set an email address?", - "This will allow you to reset your password and receive notifications.": "This will allow you to reset your password and receive notifications.", - "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", - "Skip":"Skip", - "Start verification": "Start verification", - "Share without verifying": "Share without verifying", - "Ignore request": "Ignore request", - "You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.", - "Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.", - "Encryption key request": "Encryption key request", - "Autocomplete Delay (ms):": "Autocomplete Delay (ms):" + "af": "Afrikaans", + "ar-ae": "Arabic (U.A.E.)", + "ar-bh": "Arabic (Bahrain)", + "ar-dz": "Arabic (Algeria)", + "ar-eg": "Arabic (Egypt)", + "ar-iq": "Arabic (Iraq)", + "ar-jo": "Arabic (Jordan)", + "ar-kw": "Arabic (Kuwait)", + "ar-lb": "Arabic (Lebanon)", + "ar-ly": "Arabic (Libya)", + "ar-ma": "Arabic (Morocco)", + "ar-om": "Arabic (Oman)", + "ar-qa": "Arabic (Qatar)", + "ar-sa": "Arabic (Saudi Arabia)", + "ar-sy": "Arabic (Syria)", + "ar-tn": "Arabic (Tunisia)", + "ar-ye": "Arabic (Yemen)", + "be": "Belarusian", + "bg": "Bulgarian", + "ca": "Catalan", + "cs": "Czech", + "da": "Danish", + "de-at": "German (Austria)", + "de-ch": "German (Switzerland)", + "de": "German", + "de-li": "German (Liechtenstein)", + "de-lu": "German (Luxembourg)", + "el": "Greek", + "en-au": "English (Australia)", + "en-bz": "English (Belize)", + "en-ca": "English (Canada)", + "en": "English", + "en-gb": "English (United Kingdom)", + "en-ie": "English (Ireland)", + "en-jm": "English (Jamaica)", + "en-nz": "English (New Zealand)", + "en-tt": "English (Trinidad)", + "en-us": "English (United States)", + "en-za": "English (South Africa)", + "es-ar": "Spanish (Argentina)", + "es-bo": "Spanish (Bolivia)", + "es-cl": "Spanish (Chile)", + "es-co": "Spanish (Colombia)", + "es-cr": "Spanish (Costa Rica)", + "es-do": "Spanish (Dominican Republic)", + "es-ec": "Spanish (Ecuador)", + "es-gt": "Spanish (Guatemala)", + "es-hn": "Spanish (Honduras)", + "es-mx": "Spanish (Mexico)", + "es-ni": "Spanish (Nicaragua)", + "es-pa": "Spanish (Panama)", + "es-pe": "Spanish (Peru)", + "es-pr": "Spanish (Puerto Rico)", + "es-py": "Spanish (Paraguay)", + "es": "Spanish (Spain)", + "es-sv": "Spanish (El Salvador)", + "es-uy": "Spanish (Uruguay)", + "es-ve": "Spanish (Venezuela)", + "et": "Estonian", + "eu": "Basque (Basque)", + "fa": "Farsi", + "fi": "Finnish", + "fo": "Faeroese", + "fr-be": "French (Belgium)", + "fr-ca": "French (Canada)", + "fr-ch": "French (Switzerland)", + "fr": "French", + "fr-lu": "French (Luxembourg)", + "ga": "Irish", + "gd": "Gaelic (Scotland)", + "he": "Hebrew", + "hi": "Hindi", + "hr": "Croatian", + "hu": "Hungarian", + "id": "Indonesian", + "is": "Icelandic", + "it-ch": "Italian (Switzerland)", + "it": "Italian", + "ja": "Japanese", + "ji": "Yiddish", + "ko": "Korean", + "lt": "Lithuanian", + "lv": "Latvian", + "mk": "Macedonian (FYROM)", + "ms": "Malaysian", + "mt": "Maltese", + "nl-be": "Dutch (Belgium)", + "nl": "Dutch", + "no": "Norwegian", + "pl": "Polish", + "pt-br": "Brazilian Portuguese", + "pt": "Portuguese", + "rm": "Rhaeto-Romanic", + "ro-mo": "Romanian (Republic of Moldova)", + "ro": "Romanian", + "ru-mo": "Russian (Republic of Moldova)", + "ru": "Russian", + "sb": "Sorbian", + "sk": "Slovak", + "sl": "Slovenian", + "sq": "Albanian", + "sr": "Serbian", + "sv-fi": "Swedish (Finland)", + "sv": "Swedish", + "sx": "Sutu", + "sz": "Sami (Lappish)", + "th": "Thai", + "tn": "Tswana", + "tr": "Turkish", + "ts": "Tsonga", + "uk": "Ukrainian", + "ur": "Urdu", + "ve": "Venda", + "vi": "Vietnamese", + "xh": "Xhosa", + "zh-cn": "Chinese (PRC)", + "zh-hk": "Chinese (Hong Kong SAR)", + "zh-sg": "Chinese (Singapore)", + "zh-tw": "Chinese (Taiwan)", + "zu": "Zulu", + "a room": "a room", + "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", + "Accept": "Accept", + "%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.", + "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", + "Account": "Account", + "Access Token:": "Access Token:", + "Active call (%(roomName)s)": "Active call (%(roomName)s)", + "Add": "Add", + "Add a topic": "Add a topic", + "Add email address": "Add email address", + "Add phone number": "Add phone number", + "Admin": "Admin", + "Admin tools": "Admin tools", + "And %(count)s more...": "And %(count)s more...", + "VoIP": "VoIP", + "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", + "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", + "Default Device": "Default Device", + "Microphone": "Microphone", + "Camera": "Camera", + "Advanced": "Advanced", + "Algorithm": "Algorithm", + "Hide removed messages": "Hide removed messages", + "Always show message timestamps": "Always show message timestamps", + "Authentication": "Authentication", + "Alias (optional)": "Alias (optional)", + "all room members": "all room members", + "all room members, from the point they are invited": "all room members, from the point they are invited", + "all room members, from the point they joined": "all room members, from the point they joined", + "and": "and", + "%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others", + "%(items)s and one other": "%(items)s and one other", + "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", + "and %(overflowCount)s others...": "and %(overflowCount)s others...", + "and one other...": "and one other...", + "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", + "%(names)s and one other are typing": "%(names)s and one other are typing", + "%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing", + "An email has been sent to": "An email has been sent to", + "A new password must be entered.": "A new password must be entered.", + "%(senderName)s answered the call.": "%(senderName)s answered the call.", + "anyone": "anyone", + "An error has occurred.": "An error has occurred.", + "Anyone": "Anyone", + "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests", + "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests", + "Are you sure?": "Are you sure?", + "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", + "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", + "Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?", + "Attachment": "Attachment", + "Autoplay GIFs and videos": "Autoplay GIFs and videos", + "%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.", + "Ban": "Ban", + "Banned users": "Banned users", + "Bans user with given id": "Bans user with given id", + "Blacklisted": "Blacklisted", + "Bug Report": "Bug Report", + "Bulk Options": "Bulk Options", + "Call Timeout": "Call Timeout", + "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", + "Can't load user settings": "Can't load user settings", + "Change Password": "Change Password", + "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.", + "%(senderName)s changed their profile picture.": "%(senderName)s changed their profile picture.", + "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", + "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s changed the room name to %(roomName)s.", + "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s removed the room name.", + "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".", + "Changes to who can read history will only apply to future messages in this room": "Changes to who can read history will only apply to future messages in this room", + "Changes your display nickname": "Changes your display nickname", + "changing room on a RoomView is not supported": "changing room on a RoomView is not supported", + "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.": "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.", + "Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key", + "Clear Cache and Reload": "Clear Cache and Reload", + "Clear Cache": "Clear Cache", + "Click here to join the discussion!": "Click here to join the discussion!", + "Click here to fix": "Click here to fix", + "Click to mute audio": "Click to mute audio", + "Click to mute video": "Click to mute video", + "click to reveal": "click to reveal", + "Click to unmute video": "Click to unmute video", + "Click to unmute audio": "Click to unmute audio", + "Close": "Close", + "Command error": "Command error", + "Commands": "Commands", + "Conference call failed.": "Conference call failed.", + "Conference calling is in development and may not be reliable.": "Conference calling is in development and may not be reliable.", + "Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms", + "Conference calls are not supported in this client": "Conference calls are not supported in this client", + "Confirm password": "Confirm password", + "Confirm your new password": "Confirm your new password", + "Continue": "Continue", + "Could not connect to the integration server": "Could not connect to the integration server", + "%(count)s new messages": { + "one": "%(count)s new message", + "other": "%(count)s new messages" + }, + "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", + "Create an account": "Create an account", + "Create Room": "Create Room", + "Cryptography": "Cryptography", + "Current password": "Current password", + "Curve25519 identity key": "Curve25519 identity key", + "Custom": "Custom", + "Custom level": "Custom level", + "/ddg is not a command": "/ddg is not a command", + "Deactivate Account": "Deactivate Account", + "Deactivate my account": "Deactivate my account", + "Decline": "Decline", + "Decrypt %(text)s": "Decrypt %(text)s", + "Decryption error": "Decryption error", + "Delete": "Delete", + "demote": "demote", + "Deops user with given id": "Deops user with given id", + "Default": "Default", + "Define the power level of a user": "Define the power level of a user", + "Device already verified!": "Device already verified!", + "Device ID": "Device ID", + "Device ID:": "Device ID:", + "device id: ": "device id: ", + "Device key:": "Device key:", + "Devices": "Devices", + "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room", + "Direct Chat": "Direct Chat", + "Direct chats": "Direct chats", + "Disable Notifications": "Disable Notifications", + "disabled": "disabled", + "Disable inline URL previews by default": "Disable inline URL previews by default", + "Disable markdown formatting": "Disable markdown formatting", + "Disinvite": "Disinvite", + "Display name": "Display name", + "Displays action": "Displays action", + "Don't send typing notifications": "Don't send typing notifications", + "Download %(text)s": "Download %(text)s", + "Drop File Here": "Drop File Here", + "Drop here %(toAction)s": "Drop here %(toAction)s", + "Drop here to tag %(section)s": "Drop here to tag %(section)s", + "Ed25519 fingerprint": "Ed25519 fingerprint", + "Email": "Email", + "Email address": "Email address", + "Email address (optional)": "Email address (optional)", + "Email, name or matrix ID": "Email, name or matrix ID", + "Emoji": "Emoji", + "Enable automatic language detection for syntax highlighting": "Enable automatic language detection for syntax highlighting", + "Enable encryption": "Enable encryption", + "Enable Notifications": "Enable Notifications", + "enabled": "enabled", + "Encrypted by a verified device": "Encrypted by a verified device", + "Encrypted by an unverified device": "Encrypted by an unverified device", + "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption", + "Encrypted room": "Encrypted room", + "Encryption is enabled in this room": "Encryption is enabled in this room", + "Encryption is not enabled in this room": "Encryption is not enabled in this room", + "%(senderName)s ended the call.": "%(senderName)s ended the call.", + "End-to-end encryption information": "End-to-end encryption information", + "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable", + "Enter Code": "Enter Code", + "Enter passphrase": "Enter passphrase", + "Error": "Error", + "Error decrypting attachment": "Error decrypting attachment", + "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", + "Event information": "Event information", + "Existing Call": "Existing Call", + "Export": "Export", + "Export E2E room keys": "Export E2E room keys", + "Failed to ban user": "Failed to ban user", + "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", + "Failed to change power level": "Failed to change power level", + "Failed to delete device": "Failed to delete device", + "Failed to fetch avatar URL": "Failed to fetch avatar URL", + "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", + "Failed to join room": "Failed to join room", + "Failed to join the room": "Failed to join the room", + "Failed to kick": "Failed to kick", + "Failed to leave room": "Failed to leave room", + "Failed to load timeline position": "Failed to load timeline position", + "Failed to lookup current room": "Failed to lookup current room", + "Failed to mute user": "Failed to mute user", + "Failed to register as guest:": "Failed to register as guest:", + "Failed to reject invite": "Failed to reject invite", + "Failed to reject invitation": "Failed to reject invitation", + "Failed to save settings": "Failed to save settings", + "Failed to send email": "Failed to send email", + "Failed to send request.": "Failed to send request.", + "Failed to set avatar.": "Failed to set avatar.", + "Failed to set display name": "Failed to set display name", + "Failed to set up conference call": "Failed to set up conference call", + "Failed to toggle moderator status": "Failed to toggle moderator status", + "Failed to unban": "Failed to unban", + "Failed to upload file": "Failed to upload file", + "Failed to upload profile picture!": "Failed to upload profile picture!", + "Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email", + "Failure to create room": "Failure to create room", + "Favourite": "Favourite", + "favourite": "favourite", + "Favourites": "Favourites", + "Fill screen": "Fill screen", + "Filter room members": "Filter room members", + "Forget room": "Forget room", + "Forgot your password?": "Forgot your password?", + "For security, this session has been signed out. Please sign in again.": "For security, this session has been signed out. Please sign in again.", + "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.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.", + "Found a bug?": "Found a bug?", + "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", + "Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.", + "Guests can't set avatars. Please register.": "Guests can't set avatars. Please register.", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Guest users can't create new rooms. Please register to create room and start a chat.", + "Guest users can't upload files. Please register to upload.": "Guest users can't upload files. Please register to upload.", + "Guests can't use labs features. Please register.": "Guests can't use labs features. Please register.", + "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", + "had": "had", + "Hangup": "Hangup", + "Hide read receipts": "Hide read receipts", + "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", + "Historical": "Historical", + "Home": "Home", + "Homeserver is": "Homeserver is", + "Identity Server is": "Identity Server is", + "I have verified my email address": "I have verified my email address", + "Import": "Import", + "Import E2E room keys": "Import E2E room keys", + "Incoming call from %(name)s": "Incoming call from %(name)s", + "Incoming video call from %(name)s": "Incoming video call from %(name)s", + "Incoming voice call from %(name)s": "Incoming voice call from %(name)s", + "Incorrect username and/or password.": "Incorrect username and/or password.", + "Incorrect verification code": "Incorrect verification code", + "Interface Language": "Interface Language", + "Invalid alias format": "Invalid alias format", + "Invalid address format": "Invalid address format", + "Invalid Email Address": "Invalid Email Address", + "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", + "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", + "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", + "%(displayName)s is typing": "%(displayName)s is typing", + "Sign in with": "Sign in with", + "Join as voice or video.": "Join as voice or video.", + "Join Room": "Join Room", + "joined and left": "joined and left", + "joined": "joined", + "%(targetName)s joined the room.": "%(targetName)s joined the room.", + "Joins room with given alias": "Joins room with given alias", + "Jump to first unread message.": "Jump to first unread message.", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.", + "Kick": "Kick", + "Kicks user with given id": "Kicks user with given id", + "Labs": "Labs", + "Last seen": "Last seen", + "Leave room": "Leave room", + "left and rejoined": "left and rejoined", + "left": "left", + "%(targetName)s left the room.": "%(targetName)s left the room.", + "Level:": "Level:", + "List this room in %(domain)s's room directory?": "List this room in %(domain)s's room directory?", + "Local addresses for this room:": "Local addresses for this room:", + "Logged in as:": "Logged in as:", + "Login as guest": "Login as guest", + "Logout": "Logout", + "Low priority": "Low priority", + "%(senderName)s made future room history visible to": "%(senderName)s made future room history visible to", + "Manage Integrations": "Manage Integrations", + "Markdown is disabled": "Markdown is disabled", + "Markdown is enabled": "Markdown is enabled", + "matrix-react-sdk version:": "matrix-react-sdk version:", + "Members only": "Members only", + "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", + "Missing room_id in request": "Missing room_id in request", + "Missing user_id in request": "Missing user_id in request", + "Mobile phone number": "Mobile phone number", + "Mobile phone number (optional)": "Mobile phone number (optional)", + "Moderator": "Moderator", + "Must be viewing a room": "Must be viewing a room", + "Mute": "Mute", + "my Matrix ID": "my Matrix ID", + "Name": "Name", + "Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device", + "Never send encrypted messages to unverified devices in this room": "Never send encrypted messages to unverified devices in this room", + "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", + "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", + "New Composer & Autocomplete": "New Composer & Autocomplete", + "Matrix Apps": "Matrix Apps", + "New password": "New password", + "New passwords don't match": "New passwords don't match", + "New passwords must match each other.": "New passwords must match each other.", + "none": "none", + "not set": "not set", + "not specified": "not specified", + "Notifications": "Notifications", + "(not supported by this browser)": "(not supported by this browser)", + "": "", + "NOT verified": "NOT verified", + "No devices with registered encryption keys": "No devices with registered encryption keys", + "No display name": "No display name", + "No more results": "No more results", + "No results": "No results", + "No users have specific privileges in this room": "No users have specific privileges in this room", + "OK": "OK", + "olm version:": "olm version:", + "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)", + "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", + "Only people who have been invited": "Only people who have been invited", + "Operation failed": "Operation failed", + "Otherwise, click here to send a bug report.": "Otherwise, click here to send a bug report.", + "Password": "Password", + "Password:": "Password:", + "Passwords can't be empty": "Passwords can't be empty", + "People": "People", + "Permissions": "Permissions", + "Phone": "Phone", + "%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.", + "Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.", + "Please Register": "Please Register", + "Power level must be positive integer.": "Power level must be positive integer.", + "Press": "Press", + "Press to start a chat with someone": "Press to start a chat with someone", + "Privacy warning": "Privacy warning", + "Private Chat": "Private Chat", + "Privileged Users": "Privileged Users", + "Profile": "Profile", + "Public Chat": "Public Chat", + "Reason": "Reason", + "Reason: %(reasonText)s": "Reason: %(reasonText)s", + "Revoke Moderator": "Revoke Moderator", + "Refer a friend to Riot:": "Refer a friend to Riot:", + "Register": "Register", + "rejected": "rejected", + "%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.", + "Reject invitation": "Reject invitation", + "Rejoin": "Rejoin", + "Remote addresses for this room:": "Remote addresses for this room:", + "Remove Contact Information?": "Remove Contact Information?", + "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).", + "%(senderName)s removed their profile picture.": "%(senderName)s removed their profile picture.", + "Remove": "Remove", + "Remove %(threePid)s?": "Remove %(threePid)s?", + "%(senderName)s requested a VoIP conference.": "%(senderName)s requested a VoIP conference.", + "Report it": "Report it", + "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", + "restore": "restore", + "Results from DuckDuckGo": "Results from DuckDuckGo", + "Return to app": "Return to app", + "Return to login screen": "Return to login screen", + "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings", + "Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again", + "riot-web version:": "riot-web version:", + "Room %(roomId)s not visible": "Room %(roomId)s not visible", + "Room Colour": "Room Colour", + "Room contains unknown devices": "Room contains unknown devices", + "Room name (optional)": "Room name (optional)", + "%(roomName)s does not exist.": "%(roomName)s does not exist.", + "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", + "Rooms": "Rooms", + "Save": "Save", + "Scroll to bottom of page": "Scroll to bottom of page", + "Scroll to unread messages": "Scroll to unread messages", + "Search": "Search", + "Search failed": "Search failed", + "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", + "Searching known users": "Searching known users", + "Seen by %(userName)s at %(dateTime)s": "Seen by %(userName)s at %(dateTime)s", + "Send a message (unencrypted)": "Send a message (unencrypted)", + "Send an encrypted message": "Send an encrypted message", + "Send anyway": "Send anyway", + "Sender device information": "Sender device information", + "Send Invites": "Send Invites", + "Send Reset Email": "Send Reset Email", + "sent an image": "sent an image", + "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s sent an image.", + "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.", + "sent a video": "sent a video", + "Server error": "Server error", + "Server may be unavailable or overloaded": "Server may be unavailable or overloaded", + "Server may be unavailable, overloaded, or search timed out :(": "Server may be unavailable, overloaded, or search timed out :(", + "Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big", + "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", + "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", + "Session ID": "Session ID", + "%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.", + "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", + "Set": "Set", + "Settings": "Settings", + "Show panel": "Show panel", + "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", + "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", + "Signed Out": "Signed Out", + "Sign in": "Sign in", + "Sign out": "Sign out", + "since the point in time of selecting this option": "since the point in time of selecting this option", + "since they joined": "since they joined", + "since they were invited": "since they were invited", + "Some of your messages have not been sent.": "Some of your messages have not been sent.", + "Someone": "Someone", + "Sorry, this homeserver is using a login which is not recognised ": "Sorry, this homeserver is using a login which is not recognised ", + "Start a chat": "Start a chat", + "Start authentication": "Start authentication", + "Start Chat": "Start Chat", + "Submit": "Submit", + "Success": "Success", + "tag as %(tagName)s": "tag as %(tagName)s", + "tag direct chat": "tag direct chat", + "Tagged as: ": "Tagged as: ", + "The default role for new room members is": "The default role for new room members is", + "The main address for this room is": "The main address for this room is", + "The phone number entered looks invalid": "The phone number entered looks invalid", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", + "This action cannot be performed by a guest user. Please register to be able to do this.": "This action cannot be performed by a guest user. Please register to be able to do this.", + "This email address is already in use": "This email address is already in use", + "This email address was not found": "This email address was not found", + "%(actionVerb)s this person?": "%(actionVerb)s this person?", + "The email address linked to your account must be entered.": "The email address linked to your account must be entered.", + "The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads", + "The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload", + "The remote side failed to pick up": "The remote side failed to pick up", + "This Home Server does not support login using email address.": "This Home Server does not support login using email address.", + "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", + "There was a problem logging in.": "There was a problem logging in.", + "This room has no local addresses": "This room has no local addresses", + "This room is not recognised.": "This room is not recognised.", + "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", + "The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged", + "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address", + "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", + "This phone number is already in use": "This phone number is already in use", + "This room": "This room", + "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", + "This room's internal ID is": "This room's internal ID is", + "times": "times", + "To ban users": "To ban users", + "to browse the directory": "to browse the directory", + "To configure the room": "To configure the room", + "to demote": "to demote", + "to favourite": "to favourite", + "To invite users into the room": "To invite users into the room", + "To kick users": "To kick users", + "To link to a room it must have an address.": "To link to a room it must have an address.", + "to make a room or": "to make a room or", + "To remove other users' messages": "To remove other users' messages", + "To reset your password, enter the email address linked to your account": "To reset your password, enter the email address linked to your account", + "to restore": "to restore", + "To send events of type": "To send events of type", + "To send messages": "To send messages", + "to start a chat with someone": "to start a chat with someone", + "to tag as %(tagName)s": "to tag as %(tagName)s", + "to tag direct chat": "to tag direct chat", + "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", + "Turn Markdown off": "Turn Markdown off", + "Turn Markdown on": "Turn Markdown on", + "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).", + "Unable to add email address": "Unable to add email address", + "Unable to create widget.": "Unable to create widget.", + "Unable to remove contact information": "Unable to remove contact information", + "Unable to restore previous session": "Unable to restore previous session", + "Unable to verify email address.": "Unable to verify email address.", + "Unban": "Unban", + "Unbans user with given id": "Unbans user with given id", + "%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.", + "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", + "Unable to capture screen": "Unable to capture screen", + "Unable to enable Notifications": "Unable to enable Notifications", + "Unable to load device list": "Unable to load device list", + "Undecryptable": "Undecryptable", + "Unencrypted room": "Unencrypted room", + "unencrypted": "unencrypted", + "Unencrypted message": "Unencrypted message", + "unknown caller": "unknown caller", + "Unknown command": "Unknown command", + "unknown device": "unknown device", + "unknown error code": "unknown error code", + "Unknown room %(roomId)s": "Unknown room %(roomId)s", + "Unknown (user, device) pair:": "Unknown (user, device) pair:", + "unknown": "unknown", + "Unmute": "Unmute", + "Unnamed Room": "Unnamed Room", + "Unrecognised command:": "Unrecognised command:", + "Unrecognised room alias:": "Unrecognised room alias:", + "Unverified": "Unverified", + "Uploading %(filename)s and %(count)s others": { + "zero": "Uploading %(filename)s", + "one": "Uploading %(filename)s and %(count)s other", + "other": "Uploading %(filename)s and %(count)s others" + }, + "uploaded a file": "uploaded a file", + "Upload avatar": "Upload avatar", + "Upload Failed": "Upload Failed", + "Upload Files": "Upload Files", + "Upload file": "Upload file", + "Upload new:": "Upload new:", + "Usage": "Usage", + "Use compact timeline layout": "Use compact timeline layout", + "Use with caution": "Use with caution", + "User ID": "User ID", + "User Interface": "User Interface", + "%(user)s is a": "%(user)s is a", + "User name": "User name", + "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)", + "Username invalid: %(errMessage)s": "Username invalid: %(errMessage)s", + "Users": "Users", + "User": "User", + "Verification Pending": "Verification Pending", + "Verification": "Verification", + "verified": "verified", + "Verified": "Verified", + "Verified key": "Verified key", + "Video call": "Video call", + "Voice call": "Voice call", + "VoIP conference finished.": "VoIP conference finished.", + "VoIP conference started.": "VoIP conference started.", + "VoIP is unsupported": "VoIP is unsupported", + "(could not connect media)": "(could not connect media)", + "(no answer)": "(no answer)", + "(unknown failure: %(reason)s)": "(unknown failure: %(reason)s)", + "(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)", + "Warning!": "Warning!", + "WARNING: Device already verified, but keys do NOT MATCH!": "WARNING: Device already verified, but keys do NOT MATCH!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "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!", + "Who can access this room?": "Who can access this room?", + "Who can read history?": "Who can read history?", + "Who would you like to add to this room?": "Who would you like to add to this room?", + "Who would you like to communicate with?": "Who would you like to communicate with?", + "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", + "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", + "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", + "You are already in a call.": "You are already in a call.", + "You're not in any rooms yet! Press to make a room or to browse the directory": "You're not in any rooms yet! Press to make a room or to browse the directory", + "You are trying to access %(roomName)s.": "You are trying to access %(roomName)s.", + "You cannot place a call with yourself.": "You cannot place a call with yourself.", + "You cannot place VoIP calls in this browser.": "You cannot place VoIP calls in this browser.", + "You do not have permission to post to this room": "You do not have permission to post to this room", + "You have been banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", + "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s", + "You have been kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)s.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device", + "You have disabled URL previews by default.": "You have disabled URL previews by default.", + "You have enabled URL previews by default.": "You have enabled URL previews by default.", + "You have entered an invalid contact. Try using their Matrix ID or email address.": "You have entered an invalid contact. Try using their Matrix ID or email address.", + "You have no visible notifications": "You have no visible notifications", + "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", + "you must be a": "you must be a", + "You must register to use this functionality": "You must register to use this functionality", + "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", + "You need to be logged in.": "You need to be logged in.", + "You need to enter a user name.": "You need to enter a user name.", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", + "Your password has been reset": "Your password has been reset", + "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them", + "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?", + "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?", + "You should not yet trust it to secure data": "You should not yet trust it to secure data", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.", + "Your home server does not support device management.": "Your home server does not support device management.", + "Sun": "Sun", + "Mon": "Mon", + "Tue": "Tue", + "Wed": "Wed", + "Thu": "Thu", + "Fri": "Fri", + "Sat": "Sat", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "May", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Oct", + "Nov": "Nov", + "Dec": "Dec", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", + "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", + "Set a display name:": "Set a display name:", + "Set a Display Name": "Set a Display Name", + "Upload an avatar:": "Upload an avatar:", + "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", + "Missing password.": "Missing password.", + "Passwords don't match.": "Passwords don't match.", + "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Password too short (min %(MIN_PASSWORD_LENGTH)s).", + "This doesn't look like a valid email address.": "This doesn't look like a valid email address.", + "This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.", + "User names may only contain letters, numbers, dots, hyphens and underscores.": "User names may only contain letters, numbers, dots, hyphens and underscores.", + "An unknown error occurred.": "An unknown error occurred.", + "I already have an account": "I already have an account", + "An error occurred: %(error_string)s": "An error occurred: %(error_string)s", + "Topic": "Topic", + "Make Moderator": "Make Moderator", + "Make this room private": "Make this room private", + "Share message history with new users": "Share message history with new users", + "Encrypt room": "Encrypt room", + "There are no visible files in this room": "There are no visible files in this room", + "Room": "Room", + "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", + "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.", + "Auto-complete": "Auto-complete", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Resend all or cancel all now. You can also select individual messages to resend or cancel.", + "(~%(count)s results)": { + "one": "(~%(count)s result)", + "other": "(~%(count)s results)" + }, + "Cancel": "Cancel", + "or": "or", + "Active call": "Active call", + "Monday": "Monday", + "Tuesday": "Tuesday", + "Wednesday": "Wednesday", + "Thursday": "Thursday", + "Friday": "Friday", + "Saturday": "Saturday", + "Sunday": "Sunday", + "bold": "bold", + "italic": "italic", + "strike": "strike", + "underline": "underline", + "code": "code", + "quote": "quote", + "bullet": "bullet", + "numbullet": "numbullet", + "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times", + "%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times", + "%(severalUsers)sjoined": "%(severalUsers)sjoined", + "%(oneUser)sjoined": "%(oneUser)sjoined", + "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times", + "%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times", + "%(severalUsers)sleft": "%(severalUsers)sleft", + "%(oneUser)sleft": "%(oneUser)sleft", + "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times", + "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times", + "%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left", + "%(oneUser)sjoined and left": "%(oneUser)sjoined and left", + "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times", + "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times", + "%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined", + "%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined", + "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times", + "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times", + "%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations", + "%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation", + "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times", + "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times", + "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn", + "%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn", + "were invited %(repeats)s times": "were invited %(repeats)s times", + "was invited %(repeats)s times": "was invited %(repeats)s times", + "were invited": "were invited", + "was invited": "was invited", + "were banned %(repeats)s times": "were banned %(repeats)s times", + "was banned %(repeats)s times": "was banned %(repeats)s times", + "were banned": "were banned", + "was banned": "was banned", + "were unbanned %(repeats)s times": "were unbanned %(repeats)s times", + "was unbanned %(repeats)s times": "was unbanned %(repeats)s times", + "were unbanned": "were unbanned", + "was unbanned": "was unbanned", + "were kicked %(repeats)s times": "were kicked %(repeats)s times", + "was kicked %(repeats)s times": "was kicked %(repeats)s times", + "were kicked": "were kicked", + "was kicked": "was kicked", + "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times", + "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times", + "%(severalUsers)schanged their name": "%(severalUsers)schanged their name", + "%(oneUser)schanged their name": "%(oneUser)schanged their name", + "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times", + "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times", + "%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar", + "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar", + "Please select the destination room for this message": "Please select the destination room for this message", + "Create new room": "Create new room", + "Welcome page": "Welcome page", + "Room directory": "Room directory", + "Start chat": "Start chat", + "New Password": "New Password", + "Start automatically after system login": "Start automatically after system login", + "Desktop specific": "Desktop specific", + "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.", + "Passphrases must match": "Passphrases must match", + "Passphrase must not be empty": "Passphrase must not be empty", + "Export room keys": "Export room keys", + "Confirm passphrase": "Confirm passphrase", + "Import room keys": "Import room keys", + "File to import": "File to import", + "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "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", + "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.", + "Failed to invite": "Failed to invite", + "Failed to invite user": "Failed to invite user", + "Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:", + "Confirm Removal": "Confirm Removal", + "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", + "Unknown error": "Unknown error", + "Incorrect password": "Incorrect password", + "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "This will make your account permanently unusable. You will not be able to re-register the same user ID.", + "This action is irreversible.": "This action is irreversible.", + "To continue, please enter your password.": "To continue, please enter your password.", + "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:", + "Device name": "Device name", + "Device Name": "Device Name", + "Device key": "Device key", + "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.": "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.", + "In future this verification process will be more sophisticated.": "In future this verification process will be more sophisticated.", + "Verify device": "Verify device", + "I verify that the keys match": "I verify that the keys match", + "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.", + "Unable to restore session": "Unable to restore session", + "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "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.", + "Continue anyway": "Continue anyway", + "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?", + "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.", + "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.", + "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", + "Unknown devices": "Unknown devices", + "Unknown Address": "Unknown Address", + "Unblacklist": "Unblacklist", + "Blacklist": "Blacklist", + "Unverify": "Unverify", + "Verify...": "Verify...", + "ex. @bob:example.com": "ex. @bob:example.com", + "Add User": "Add User", + "This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot", + "Sign in with CAS": "Sign in with CAS", + "Custom Server Options": "Custom Server Options", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.", + "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.", + "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.", + "Dismiss": "Dismiss", + "Please check your email to continue registration.": "Please check your email to continue registration.", + "Token incorrect": "Token incorrect", + "A text message has been sent to": "A text message has been sent to", + "Please enter the code it contains:": "Please enter the code it contains:", + "powered by Matrix": "powered by Matrix", + "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", + "You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s", + "Default server": "Default server", + "Custom server": "Custom server", + "Home server URL": "Home server URL", + "Identity server URL": "Identity server URL", + "What does this mean?": "What does this mean?", + "Error decrypting audio": "Error decrypting audio", + "Error decrypting image": "Error decrypting image", + "Image '%(Body)s' cannot be displayed.": "Image '%(Body)s' cannot be displayed.", + "This image cannot be displayed.": "This image cannot be displayed.", + "Error decrypting video": "Error decrypting video", + "Add an Integration": "Add an Integration", + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "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?", + "Removed or unknown message type": "Removed or unknown message type", + "Disable URL previews by default for participants in this room": "Disable URL previews by default for participants in this room", + "Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)", + "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", + "URL Previews": "URL Previews", + "Enable URL previews for this room (affects only you)": "Enable URL previews for this room (affects only you)", + "Drop file here to upload": "Drop file here to upload", + " (unsupported)": " (unsupported)", + "Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)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", + "Updates": "Updates", + "Check for update": "Check for update", + "Start chatting": "Start chatting", + "Start Chatting": "Start Chatting", + "Click on the button below to start chatting!": "Click on the button below to start chatting!", + "$senderDisplayName changed the room avatar to ": "$senderDisplayName changed the room avatar to ", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.", + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s", + "Username available": "Username available", + "Username not available": "Username not available", + "Something went wrong!": "Something went wrong!", + "This will be your account name on the homeserver, or you can pick a different server.": "This will be your account name on the homeserver, or you can pick a different server.", + "If you already have a Matrix account you can log in instead.": "If you already have a Matrix account you can log in instead.", + "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", + "Not a valid Riot keyfile": "Not a valid Riot keyfile", + "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", + "Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls", + "Do you want to set an email address?": "Do you want to set an email address?", + "This will allow you to reset your password and receive notifications.": "This will allow you to reset your password and receive notifications.", + "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", + "Skip": "Skip", + "Start verification": "Start verification", + "Share without verifying": "Share without verifying", + "Ignore request": "Ignore request", + "You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.", + "Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.", + "Encryption key request": "Encryption key request", + "Autocomplete Delay (ms):": "Autocomplete Delay (ms):" } diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index e060add84f..30ff296065 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -378,6 +378,7 @@ "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", "New Composer & Autocomplete": "New Composer & Autocomplete", + "Matrix Apps": "Matrix Apps", "New password": "New password", "New passwords don't match": "New passwords don't match", "New passwords must match each other.": "New passwords must match each other.", From bf2a4afce523b8b96cc6b6999a8823ea209e6817 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:02:07 +0100 Subject: [PATCH 58/69] Change to allow setting of DEBUG at run-time. --- 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 6e6325504a..85440cf261 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -47,7 +47,7 @@ import UserProvider from '../../autocomplete/UserProvider'; import RoomViewStore from '../../stores/RoomViewStore'; -const DEBUG = false; +let DEBUG = false; let debuglog = function() {}; if (DEBUG) { From 06dafdc099ff60262d1318b86105c8cb2fbd2903 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:20:07 +0100 Subject: [PATCH 59/69] Remove unused state variable. --- src/components/structures/RoomView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 85440cf261..a793a61e68 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -90,7 +90,6 @@ module.exports = React.createClass({ return { room: null, roomId: null, - userId: null, roomLoading: true, peekLoading: false, shouldPeek: true, From ff1636aaf5568f8bc182d0944ab671afe43121a8 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:21:05 +0100 Subject: [PATCH 60/69] Simplify boolean assignment. --- 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 a793a61e68..ffb0c1243c 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -465,7 +465,7 @@ module.exports = React.createClass({ break; case 'appsDrawer': this.setState({ - showApps: payload.show ? true : false, + showApps: payload.show, }); break; } From ddea1f35d2ea992c300b3de00d9c46e8420f9619 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:23:33 +0100 Subject: [PATCH 61/69] Fix header. --- src/components/views/elements/AppTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index dc34d15d7f..baa001b2f2 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -1,5 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 93bdfc99df3805148c70d83bfc8594ad88b270c8 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:25:36 +0100 Subject: [PATCH 62/69] i18n "Cancel" alt text. --- src/components/views/elements/AppTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index baa001b2f2..d92fcf580f 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -87,7 +87,7 @@ export default React.createClass({ {/* Delete widget */} Cancel From 481a66ef3c12d48067f64c6ef43b0710376ddf11 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:26:05 +0100 Subject: [PATCH 63/69] Fix header. --- src/components/views/rooms/AppsDrawer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 8919fd6f74..202d1c0018 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -1,5 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From e752cc8557754afb1a652cdc81fb49ba1351a972 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:32:38 +0100 Subject: [PATCH 64/69] Use 'this' in preference to local reference. --- src/components/views/rooms/AppsDrawer.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 202d1c0018..ce414b2ea0 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -40,13 +40,12 @@ module.exports = React.createClass({ componentDidMount: function() { this.scalarClient = null; - const appsDrawer = this; if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) { this.scalarClient = new ScalarAuthClient(); this.scalarClient.connect().done(() => { this.forceUpdate(); - if (appsDrawer.state.apps && appsDrawer.state.apps.length < 1) { - appsDrawer.onClickAddWidget(); + if (this.state.apps && this.state.apps.length < 1) { + this.onClickAddWidget(); } }, (err) => { this.setState({ From d06d066050fa19adc1b8b2105b963a99f4ca9915 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:54:47 +0100 Subject: [PATCH 65/69] Move getInitialState to top of file. --- src/components/views/rooms/AppsDrawer.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index ce414b2ea0..10e13eb623 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -33,6 +33,12 @@ module.exports = React.createClass({ room: React.PropTypes.object.isRequired, }, + getInitialState: function() { + return { + apps: this._getApps(), + }; + }, + componentWillMount: function() { ScalarMessaging.startListening(); MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents); @@ -124,12 +130,6 @@ module.exports = React.createClass({ return app; }, - getInitialState: function() { - return { - apps: this._getApps(), - }; - }, - onRoomStateEvents: function(ev, state) { if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'im.vector.modular.widgets') { return; From e70eca0b0c928ec7ddab4db175e8be973f98564b Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 12:58:09 +0100 Subject: [PATCH 66/69] Comment unused code and add TODO to handle scalar errors. --- src/components/views/rooms/AppsDrawer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 10e13eb623..5378136f2d 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -53,10 +53,12 @@ module.exports = React.createClass({ if (this.state.apps && this.state.apps.length < 1) { this.onClickAddWidget(); } - }, (err) => { - this.setState({ - scalar_error: err, - }); + // TODO -- Handle Scalar errors + // }, + // (err) => { + // this.setState({ + // scalar_error: err, + // }); }); } }, From f8c064ec2568f24b74bce936e4a724c4857a648a Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 13:45:29 +0100 Subject: [PATCH 67/69] REmove redundant call to _getApps --- src/components/views/rooms/AppsDrawer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 5378136f2d..ff579ff91a 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -163,7 +163,7 @@ module.exports = React.createClass({ }); } this.setState({ - apps: this._getApps(), + apps: this.apps, }); }, From 3a10cda2ca30d431c8569d7415cc0626337435ee Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 13:55:18 +0100 Subject: [PATCH 68/69] Add translations. --- src/components/views/rooms/AppsDrawer.js | 6 ++++-- src/components/views/rooms/MessageComposer.js | 4 ++-- src/i18n/strings/en_EN.json | 5 ++++- src/i18n/strings/en_US.json | 5 ++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index ff579ff91a..92902a0225 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -25,6 +25,8 @@ import sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import ScalarAuthClient from '../../../ScalarAuthClient'; import ScalarMessaging from '../../../ScalarMessaging'; +import { _t } from '../../../languageHandler'; + module.exports = React.createClass({ displayName: 'AppsDrawer', @@ -205,8 +207,8 @@ module.exports = React.createClass({ role="button" tabIndex="0" className="mx_AddWidget_button" - title="Add a widget"> - [+] Add a widget + title={_t('Add a widget')}> + [+] {_t('Add a widget')}
); return ( diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 7896a03376..c83e32d9a8 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -318,12 +318,12 @@ export default class MessageComposer extends React.Component { if (UserSettingsStore.isFeatureEnabled('matrix_apps')) { if (this.props.showApps) { hideAppsButton = -
+
; } else { showAppsButton = -
+
; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b8f5c26332..397c2ecaaa 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1,4 +1,5 @@ { + "Add a widget": "Add a widget", "af": "Afrikaans", "ar-ae": "Arabic (U.A.E.)", "ar-bh": "Arabic (Bahrain)", @@ -337,6 +338,7 @@ "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", "had": "had", "Hangup": "Hangup", + "Hide Apps": "Hide Apps", "Hide read receipts": "Hide read receipts", "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", "Historical": "Historical", @@ -393,6 +395,7 @@ "Markdown is disabled": "Markdown is disabled", "Markdown is enabled": "Markdown is enabled", "matrix-react-sdk version:": "matrix-react-sdk version:", + "Matrix Apps": "Matrix Apps", "Members only": "Members only", "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", "Missing room_id in request": "Missing room_id in request", @@ -409,7 +412,6 @@ "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", "New Composer & Autocomplete": "New Composer & Autocomplete", - "Matrix Apps": "Matrix Apps", "New password": "New password", "New passwords don't match": "New passwords don't match", "New passwords must match each other.": "New passwords must match each other.", @@ -510,6 +512,7 @@ "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", "Set": "Set", "Settings": "Settings", + "Show Apps": "Show Apps", "Show panel": "Show panel", "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 30ff296065..4b8b8a5d3c 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -1,4 +1,5 @@ { + "Add a widget": "Add a widget", "af": "Afrikaans", "ar-ae": "Arabic (U.A.E.)", "ar-bh": "Arabic (Bahrain)", @@ -311,6 +312,7 @@ "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", "had": "had", "Hangup": "Hangup", + "Hide Apps": "Hide Apps", "Hide read receipts": "Hide read receipts", "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", "Historical": "Historical", @@ -362,6 +364,7 @@ "Markdown is disabled": "Markdown is disabled", "Markdown is enabled": "Markdown is enabled", "matrix-react-sdk version:": "matrix-react-sdk version:", + "Matrix Apps": "Matrix Apps", "Members only": "Members only", "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", "Missing room_id in request": "Missing room_id in request", @@ -378,7 +381,6 @@ "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", "New Composer & Autocomplete": "New Composer & Autocomplete", - "Matrix Apps": "Matrix Apps", "New password": "New password", "New passwords don't match": "New passwords don't match", "New passwords must match each other.": "New passwords must match each other.", @@ -465,6 +467,7 @@ "%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.", "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", "Settings": "Settings", + "Show Apps": "Show Apps", "Show panel": "Show panel", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", "Signed Out": "Signed Out", From ea83d7eee229e50063d2e8803fc107cb8c1ae913 Mon Sep 17 00:00:00 2001 From: Richard Lewis Date: Wed, 28 Jun 2017 15:53:18 +0100 Subject: [PATCH 69/69] Add missing import and fix apps reference. --- src/components/views/elements/AppTile.js | 5 +++-- src/components/views/rooms/AppsDrawer.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index d92fcf580f..6f4c931ab7 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -16,8 +16,9 @@ limitations under the License. 'use strict'; -const React = require('react'); -const MatrixClientPeg = require('../../../MatrixClientPeg'); +import React from 'react'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import { _t } from '../../../languageHandler'; export default React.createClass({ displayName: 'AppTile', diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 92902a0225..6545ac1ce5 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -165,7 +165,7 @@ module.exports = React.createClass({ }); } this.setState({ - apps: this.apps, + apps: apps, }); },