Merge pull request #812 from t3chguy/forward_message

Message Forwarding
This commit is contained in:
Matthew Hodgson 2017-05-28 02:37:31 +01:00 committed by GitHub
commit accc17a39a
8 changed files with 151 additions and 41 deletions

2
.gitignore vendored
View file

@ -10,5 +10,5 @@ npm-debug.log
# test reports created by karma # test reports created by karma
/karma-reports /karma-reports
# ignore auto-generated component index /.idea
/src/component-index.js /src/component-index.js

View file

@ -22,7 +22,7 @@ let isDialogOpen = false;
const onAction = function(payload) { const onAction = function(payload) {
if (payload.action === 'unknown_device_error' && !isDialogOpen) { if (payload.action === 'unknown_device_error' && !isDialogOpen) {
var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
isDialogOpen = true; isDialogOpen = true;
Modal.createDialog(UnknownDeviceDialog, { Modal.createDialog(UnknownDeviceDialog, {
devices: payload.err.devices, devices: payload.err.devices,
@ -33,17 +33,17 @@ const onAction = function(payload) {
// https://github.com/vector-im/riot-web/issues/3148 // https://github.com/vector-im/riot-web/issues/3148
console.log('UnknownDeviceDialog closed with '+r); console.log('UnknownDeviceDialog closed with '+r);
}, },
}, "mx_Dialog_unknownDevice"); }, 'mx_Dialog_unknownDevice');
} }
} };
let ref = null; let ref = null;
export function startListening () { export function startListening() {
ref = dis.register(onAction); ref = dis.register(onAction);
} }
export function stopListening () { export function stopListening() {
if (ref) { if (ref) {
dis.unregister(ref); dis.unregister(ref);
ref = null; ref = null;

View file

@ -183,7 +183,7 @@ export default React.createClass({
ConferenceHandler={this.props.ConferenceHandler} ConferenceHandler={this.props.ConferenceHandler}
scrollStateMap={this._scrollStateMap} 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; break;
case PageTypes.UserSettings: case PageTypes.UserSettings:
@ -195,7 +195,7 @@ export default React.createClass({
referralBaseUrl={this.props.config.referralBaseUrl} referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken} 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; break;
case PageTypes.CreateRoom: case PageTypes.CreateRoom:
@ -203,7 +203,7 @@ export default React.createClass({
onRoomCreated={this.props.onRoomCreated} onRoomCreated={this.props.onRoomCreated}
collapsedRhs={this.props.collapse_rhs} 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; break;
case PageTypes.RoomDirectory: case PageTypes.RoomDirectory:
@ -219,12 +219,12 @@ export default React.createClass({
teamServerUrl={this.props.config.teamServerConfig.teamServerURL} teamServerUrl={this.props.config.teamServerConfig.teamServerURL}
teamToken={this.props.teamToken} 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; break;
case PageTypes.UserView: case PageTypes.UserView:
page_element = null; // deliberately null for now 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; break;
} }
@ -253,7 +253,7 @@ export default React.createClass({
<LeftPanel <LeftPanel
selectedRoom={this.props.currentRoomId} selectedRoom={this.props.currentRoomId}
collapsed={this.props.collapse_lhs || false} collapsed={this.props.collapse_lhs || false}
opacity={this.props.sideOpacity} opacity={this.props.leftOpacity}
teamToken={this.props.teamToken} teamToken={this.props.teamToken}
/> />
<main className='mx_MatrixChat_middlePanel'> <main className='mx_MatrixChat_middlePanel'>

View file

@ -117,8 +117,9 @@ module.exports = React.createClass({
collapse_rhs: false, collapse_rhs: false,
ready: false, ready: false,
width: 10000, width: 10000,
sideOpacity: 1.0, leftOpacity: 1.0,
middleOpacity: 1.0, middleOpacity: 1.0,
rightOpacity: 1.0,
version: null, version: null,
newVersion: null, newVersion: null,
@ -490,12 +491,14 @@ module.exports = React.createClass({
collapse_rhs: false, collapse_rhs: false,
}); });
break; break;
case 'ui_opacity': case 'ui_opacity': {
const sideDefault = payload.sideOpacity >= 0.0 ? payload.sideOpacity : 1.0;
this.setState({ this.setState({
sideOpacity: payload.sideOpacity, leftOpacity: payload.leftOpacity >= 0.0 ? payload.leftOpacity : sideDefault,
middleOpacity: payload.middleOpacity, middleOpacity: payload.middleOpacity || 1.0,
rightOpacity: payload.rightOpacity >= 0.0 ? payload.rightOpacity : sideDefault,
}); });
break; break; }
case 'set_theme': case 'set_theme':
this._onSetTheme(payload.value); this._onSetTheme(payload.value);
break; break;

View file

@ -125,6 +125,8 @@ module.exports = React.createClass({
room: null, room: null,
roomId: null, roomId: null,
roomLoading: true, roomLoading: true,
forwardingEvent: null,
editingRoomSettings: false, editingRoomSettings: false,
uploadingRoomSettings: false, uploadingRoomSettings: false,
numUnreadMessages: 0, numUnreadMessages: 0,
@ -452,6 +454,11 @@ module.exports = React.createClass({
callState: callState callState: callState
}); });
break;
case 'forward_event':
this.setState({
forwardingEvent: payload.content,
});
break; break;
} }
}, },
@ -1195,7 +1202,10 @@ module.exports = React.createClass({
onCancelClick: function() { onCancelClick: function() {
console.log("updateTint from onCancelClick"); console.log("updateTint from onCancelClick");
this.updateTint(); this.updateTint();
this.setState({editingRoomSettings: false}); this.setState({
editingRoomSettings: false,
forwardingEvent: null,
});
dis.dispatch({action: 'focus_composer'}); dis.dispatch({action: 'focus_composer'});
}, },
@ -1473,16 +1483,17 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
var RoomHeader = sdk.getComponent('rooms.RoomHeader'); const RoomHeader = sdk.getComponent('rooms.RoomHeader');
var MessageComposer = sdk.getComponent('rooms.MessageComposer'); const MessageComposer = sdk.getComponent('rooms.MessageComposer');
var RoomSettings = sdk.getComponent("rooms.RoomSettings"); const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
var AuxPanel = sdk.getComponent("rooms.AuxPanel"); const RoomSettings = sdk.getComponent("rooms.RoomSettings");
var SearchBar = sdk.getComponent("rooms.SearchBar"); const AuxPanel = sdk.getComponent("rooms.AuxPanel");
var ScrollPanel = sdk.getComponent("structures.ScrollPanel"); const SearchBar = sdk.getComponent("rooms.SearchBar");
var TintableSvg = sdk.getComponent("elements.TintableSvg"); const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); const TintableSvg = sdk.getComponent("elements.TintableSvg");
var Loader = sdk.getComponent("elements.Spinner"); const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
var TimelinePanel = sdk.getComponent("structures.TimelinePanel"); const Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
if (!this.state.room) { if (!this.state.room) {
if (this.state.roomLoading) { if (this.state.roomLoading) {
@ -1610,17 +1621,16 @@ module.exports = React.createClass({
/>; />;
} }
var aux = null; let aux = null;
if (this.state.editingRoomSettings) { 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} />; 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/>; 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}/>; 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 do have a room object for this room, but we're not currently in it.
// We may have a 3rd party invite to it. // We may have a 3rd party invite to it.
var inviterName = undefined; 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); // console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
var messagePanel = ( var messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef} <TimelinePanel ref={this._gatherTimelinePanelRef}
timelineSet={this.state.room.getUnfilteredTimelineSet()} timelineSet={this.state.room.getUnfilteredTimelineSet()}
manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)} manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
manageReadMarkers={true} manageReadMarkers={true}
hidden={hideMessagePanel} hidden={hideMessagePanel}
highlightedEventId={this.props.highlightedEventId} highlightedEventId={this.state.forwardingEvent ? this.state.forwardingEvent.getId() : this.props.highlightedEventId}
eventId={this.props.eventId} eventId={this.props.eventId}
eventPixelOffset={this.props.eventPixelOffset} eventPixelOffset={this.props.eventPixelOffset}
onScroll={ this.onMessageListScroll } onScroll={ this.onMessageListScroll }
@ -1778,7 +1787,7 @@ module.exports = React.createClass({
onSearchClick={this.onSearchClick} onSearchClick={this.onSearchClick}
onSettingsClick={this.onSettingsClick} onSettingsClick={this.onSettingsClick}
onSaveClick={this.onSettingsSaveClick} onSaveClick={this.onSettingsSaveClick}
onCancelClick={this.onCancelClick} onCancelClick={aux ? this.onCancelClick : null}
onForgetClick={ onForgetClick={
(myMember && myMember.membership === "leave") ? this.onForgetClick : null (myMember && myMember.membership === "leave") ? this.onForgetClick : null
} }

View file

@ -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>
);
},
});

View file

@ -189,6 +189,9 @@ module.exports = React.createClass({
); );
save_button = <AccessibleButton className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save</AccessibleButton>; save_button = <AccessibleButton className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save</AccessibleButton>;
}
if (this.props.onCancelClick) {
cancel_button = <CancelButton onClick={this.props.onCancelClick}/>; cancel_button = <CancelButton onClick={this.props.onCancelClick}/>;
} }

View file

@ -16,13 +16,13 @@ limitations under the License.
'use strict'; 'use strict';
var flux = require("flux"); const flux = require("flux");
class MatrixDispatcher extends flux.Dispatcher { class MatrixDispatcher extends flux.Dispatcher {
/** /**
* @param {Object} payload Required. The payload to dispatch. * @param {Object} payload Required. The payload to dispatch.
* Must contain at least an 'action' key. * 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 * synchronously. This is useful for anything triggering
* an operation that the browser requires user interaction * an operation that the browser requires user interaction
* for. * for.