diff --git a/.gitignore b/.gitignore index dcfe1c355d..b99c9f1145 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,5 @@ npm-debug.log # test reports created by karma /karma-reports -# ignore auto-generated component index +/.idea /src/component-index.js diff --git a/src/UnknownDeviceErrorHandler.js b/src/UnknownDeviceErrorHandler.js index 2aa0573e22..2b1cf23380 100644 --- a/src/UnknownDeviceErrorHandler.js +++ b/src/UnknownDeviceErrorHandler.js @@ -22,7 +22,7 @@ let isDialogOpen = false; const onAction = function(payload) { if (payload.action === 'unknown_device_error' && !isDialogOpen) { - var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); + const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog'); isDialogOpen = true; Modal.createDialog(UnknownDeviceDialog, { devices: payload.err.devices, @@ -33,17 +33,17 @@ const onAction = function(payload) { // https://github.com/vector-im/riot-web/issues/3148 console.log('UnknownDeviceDialog closed with '+r); }, - }, "mx_Dialog_unknownDevice"); + }, 'mx_Dialog_unknownDevice'); } -} +}; let ref = null; -export function startListening () { +export function startListening() { ref = dis.register(onAction); } -export function stopListening () { +export function stopListening() { if (ref) { dis.unregister(ref); ref = null; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 25ca025a23..5f1aa0d32a 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -183,7 +183,7 @@ export default React.createClass({ ConferenceHandler={this.props.ConferenceHandler} scrollStateMap={this._scrollStateMap} />; - if (!this.props.collapse_rhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.sideOpacity} />; + if (!this.props.collapse_rhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.rightOpacity} />; break; case PageTypes.UserSettings: @@ -195,7 +195,7 @@ export default React.createClass({ referralBaseUrl={this.props.config.referralBaseUrl} teamToken={this.props.teamToken} />; - if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>; + if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>; break; case PageTypes.CreateRoom: @@ -203,7 +203,7 @@ export default React.createClass({ onRoomCreated={this.props.onRoomCreated} collapsedRhs={this.props.collapse_rhs} />; - if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>; + if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>; break; case PageTypes.RoomDirectory: @@ -219,12 +219,12 @@ export default React.createClass({ teamServerUrl={this.props.config.teamServerConfig.teamServerURL} teamToken={this.props.teamToken} /> - if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/> + if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/> break; case PageTypes.UserView: page_element = null; // deliberately null for now - right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.sideOpacity} />; + right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.rightOpacity} />; break; } @@ -253,7 +253,7 @@ export default React.createClass({ <LeftPanel selectedRoom={this.props.currentRoomId} collapsed={this.props.collapse_lhs || false} - opacity={this.props.sideOpacity} + opacity={this.props.leftOpacity} teamToken={this.props.teamToken} /> <main className='mx_MatrixChat_middlePanel'> diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 625ff26604..7cbc4d3ea7 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -118,8 +118,9 @@ module.exports = React.createClass({ collapse_rhs: false, ready: false, width: 10000, - sideOpacity: 1.0, + leftOpacity: 1.0, middleOpacity: 1.0, + rightOpacity: 1.0, version: null, newVersion: null, @@ -248,7 +249,6 @@ module.exports = React.createClass({ UDEHandler.startListening(); this.focusComposer = false; - window.addEventListener("focus", this.onFocus); // this can technically be done anywhere but doing this here keeps all // the routing url path logic together. @@ -492,12 +492,14 @@ module.exports = React.createClass({ collapse_rhs: false, }); break; - case 'ui_opacity': + case 'ui_opacity': { + const sideDefault = payload.sideOpacity >= 0.0 ? payload.sideOpacity : 1.0; this.setState({ - sideOpacity: payload.sideOpacity, - middleOpacity: payload.middleOpacity, + leftOpacity: payload.leftOpacity >= 0.0 ? payload.leftOpacity : sideDefault, + middleOpacity: payload.middleOpacity || 1.0, + rightOpacity: payload.rightOpacity >= 0.0 ? payload.rightOpacity : sideDefault, }); - break; + break; } case 'set_theme': this._onSetTheme(payload.value); break; @@ -911,10 +913,6 @@ module.exports = React.createClass({ }); }, - onFocus: function(ev) { - dis.dispatch({action: 'focus_composer'}); - }, - showScreen: function(screen, params) { if (screen == 'register') { dis.dispatch({ diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 4b3b09f1fa..e939510c72 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -125,6 +125,8 @@ module.exports = React.createClass({ room: null, roomId: null, roomLoading: true, + + forwardingEvent: null, editingRoomSettings: false, uploadingRoomSettings: false, numUnreadMessages: 0, @@ -452,6 +454,11 @@ module.exports = React.createClass({ callState: callState }); + break; + case 'forward_event': + this.setState({ + forwardingEvent: payload.content, + }); break; } }, @@ -1195,7 +1202,10 @@ module.exports = React.createClass({ onCancelClick: function() { console.log("updateTint from onCancelClick"); this.updateTint(); - this.setState({editingRoomSettings: false}); + this.setState({ + editingRoomSettings: false, + forwardingEvent: null, + }); dis.dispatch({action: 'focus_composer'}); }, @@ -1473,16 +1483,17 @@ module.exports = React.createClass({ }, render: function() { - var RoomHeader = sdk.getComponent('rooms.RoomHeader'); - var MessageComposer = sdk.getComponent('rooms.MessageComposer'); - var RoomSettings = sdk.getComponent("rooms.RoomSettings"); - var AuxPanel = sdk.getComponent("rooms.AuxPanel"); - var SearchBar = sdk.getComponent("rooms.SearchBar"); - var ScrollPanel = sdk.getComponent("structures.ScrollPanel"); - var TintableSvg = sdk.getComponent("elements.TintableSvg"); - var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); - var Loader = sdk.getComponent("elements.Spinner"); - var TimelinePanel = sdk.getComponent("structures.TimelinePanel"); + const RoomHeader = sdk.getComponent('rooms.RoomHeader'); + const MessageComposer = sdk.getComponent('rooms.MessageComposer'); + const ForwardMessage = sdk.getComponent("rooms.ForwardMessage"); + const RoomSettings = sdk.getComponent("rooms.RoomSettings"); + const AuxPanel = sdk.getComponent("rooms.AuxPanel"); + const SearchBar = sdk.getComponent("rooms.SearchBar"); + const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); + const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); + const Loader = sdk.getComponent("elements.Spinner"); + const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); if (!this.state.room) { if (this.state.roomLoading) { @@ -1610,17 +1621,16 @@ module.exports = React.createClass({ />; } - var aux = null; - if (this.state.editingRoomSettings) { + let aux = null; + if (this.state.forwardingEvent !== null) { + aux = <ForwardMessage onCancelClick={this.onCancelClick} currentRoomId={this.state.room.roomId} mxEvent={this.state.forwardingEvent} />; + } else if (this.state.editingRoomSettings) { aux = <RoomSettings ref="room_settings" onSaveClick={this.onSettingsSaveClick} onCancelClick={this.onCancelClick} room={this.state.room} />; - } - else if (this.state.uploadingRoomSettings) { + } else if (this.state.uploadingRoomSettings) { aux = <Loader/>; - } - else if (this.state.searching) { + } else if (this.state.searching) { aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress } onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>; - } - else if (!myMember || myMember.membership !== "join") { + } else if (!myMember || myMember.membership !== "join") { // We do have a room object for this room, but we're not currently in it. // We may have a 3rd party invite to it. var inviterName = undefined; @@ -1733,14 +1743,13 @@ module.exports = React.createClass({ } // console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); - var messagePanel = ( <TimelinePanel ref={this._gatherTimelinePanelRef} timelineSet={this.state.room.getUnfilteredTimelineSet()} manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)} manageReadMarkers={true} hidden={hideMessagePanel} - highlightedEventId={this.props.highlightedEventId} + highlightedEventId={this.state.forwardingEvent ? this.state.forwardingEvent.getId() : this.props.highlightedEventId} eventId={this.props.eventId} eventPixelOffset={this.props.eventPixelOffset} onScroll={ this.onMessageListScroll } @@ -1778,7 +1787,7 @@ module.exports = React.createClass({ onSearchClick={this.onSearchClick} onSettingsClick={this.onSettingsClick} onSaveClick={this.onSettingsSaveClick} - onCancelClick={this.onCancelClick} + onCancelClick={aux ? this.onCancelClick : null} onForgetClick={ (myMember && myMember.membership === "leave") ? this.onForgetClick : null } diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 993586df84..f4bf8b18cb 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -545,12 +545,14 @@ module.exports = React.createClass({ ); }, - onLanguageChange: function(l) { - UserSettingsStore.setLocalSetting('language', l); - this.setState({ - language: l, - }); - PlatformPeg.get().reload(); + onLanguageChange: function(newLang) { + if(this.state.language !== newLang) { + UserSettingsStore.setLocalSetting('language', newLang); + this.setState({ + language: newLang, + }); + PlatformPeg.get().reload(); + } }, _renderLanguageSetting: function () { diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js new file mode 100644 index 0000000000..0ebcdf1e4a --- /dev/null +++ b/src/components/views/rooms/ForwardMessage.js @@ -0,0 +1,95 @@ +/* + Copyright 2017 Vector Creations Ltd + Copyright 2017 Michael Telatynski + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import React from 'react'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import dis from '../../../dispatcher'; +import KeyCode from '../../../KeyCode'; + + +module.exports = React.createClass({ + displayName: 'ForwardMessage', + + propTypes: { + currentRoomId: React.PropTypes.string.isRequired, + + /* the MatrixEvent to be forwarded */ + mxEvent: React.PropTypes.object.isRequired, + + onCancelClick: React.PropTypes.func.isRequired, + }, + + componentWillMount: function() { + dis.dispatch({ + action: 'ui_opacity', + leftOpacity: 1.0, + rightOpacity: 0.3, + middleOpacity: 0.5, + }); + }, + + componentDidMount: function() { + this.dispatcherRef = dis.register(this.onAction); + document.addEventListener('keydown', this._onKeyDown); + }, + + componentWillUnmount: function() { + dis.dispatch({ + action: 'ui_opacity', + sideOpacity: 1.0, + middleOpacity: 1.0, + }); + dis.unregister(this.dispatcherRef); + document.removeEventListener('keydown', this._onKeyDown); + }, + + onAction: function(payload) { + if (payload.action === 'view_room') { + const event = this.props.mxEvent; + const Client = MatrixClientPeg.get(); + Client.sendEvent(payload.room_id, event.getType(), event.getContent()).done(() => { + dis.dispatch({action: 'message_sent'}); + }, (err) => { + if (err.name === "UnknownDeviceError") { + dis.dispatch({ + action: 'unknown_device_error', + err: err, + room: Client.getRoom(payload.room_id), + }); + } + dis.dispatch({action: 'message_send_failed'}); + }); + if (this.props.currentRoomId === payload.room_id) this.props.onCancelClick(); + } + }, + + _onKeyDown: function(ev) { + switch (ev.keyCode) { + case KeyCode.ESCAPE: + this.props.onCancelClick(); + break; + } + }, + + render: function() { + return ( + <div className="mx_ForwardMessage"> + <h1>Please select the destination room for this message</h1> + </div> + ); + }, +}); diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 3463ef948f..0ef69daba3 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -189,6 +189,9 @@ module.exports = React.createClass({ ); save_button = <AccessibleButton className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save</AccessibleButton>; + } + + if (this.props.onCancelClick) { cancel_button = <CancelButton onClick={this.props.onCancelClick}/>; } diff --git a/src/dispatcher.js b/src/dispatcher.js index 9864cb3807..f3ebed8357 100644 --- a/src/dispatcher.js +++ b/src/dispatcher.js @@ -16,13 +16,13 @@ limitations under the License. 'use strict'; -var flux = require("flux"); +const flux = require("flux"); class MatrixDispatcher extends flux.Dispatcher { /** * @param {Object} payload Required. The payload to dispatch. * Must contain at least an 'action' key. - * @param {boolean} sync Optional. Pass true to dispatch + * @param {boolean=} sync Optional. Pass true to dispatch * synchronously. This is useful for anything triggering * an operation that the browser requires user interaction * for.