Extract message options button to action bar
This adds a new action bar component to hold multiple per-message actions. This existing options button has moved to this new component, and is currently the only action.
This commit is contained in:
parent
530c92e03d
commit
ed8bbc7082
6 changed files with 148 additions and 61 deletions
87
src/components/views/messages/MessageActionBar.js
Normal file
87
src/components/views/messages/MessageActionBar.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import { createMenu } from '../../structures/ContextualMenu';
|
||||
|
||||
export default class MessageActionBar extends React.PureComponent {
|
||||
static propTypes = {
|
||||
mxEvent: PropTypes.object.isRequired,
|
||||
permalinkCreator: PropTypes.object,
|
||||
tile: PropTypes.element,
|
||||
replyThread: PropTypes.element,
|
||||
onFocusChange: PropTypes.func,
|
||||
};
|
||||
|
||||
onFocusChange = (focused) => {
|
||||
if (!this.props.onFocusChange) {
|
||||
return;
|
||||
}
|
||||
this.props.onFocusChange(focused);
|
||||
}
|
||||
|
||||
onCryptoClicked = () => {
|
||||
const event = this.props.mxEvent;
|
||||
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '',
|
||||
import('../../../async-components/views/dialogs/EncryptedEventDialog'),
|
||||
{event},
|
||||
);
|
||||
}
|
||||
|
||||
onOptionsClicked = (ev) => {
|
||||
const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
|
||||
const buttonRect = ev.target.getBoundingClientRect();
|
||||
|
||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||
const x = buttonRect.right + window.pageXOffset;
|
||||
const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
|
||||
|
||||
const {tile, replyThread} = this.props;
|
||||
|
||||
let e2eInfoCallback = null;
|
||||
if (this.props.mxEvent.isEncrypted()) {
|
||||
e2eInfoCallback = () => this.onCryptoClicked();
|
||||
}
|
||||
|
||||
createMenu(MessageContextMenu, {
|
||||
chevronOffset: 10,
|
||||
mxEvent: this.props.mxEvent,
|
||||
left: x,
|
||||
top: y,
|
||||
permalinkCreator: this.props.permalinkCreator,
|
||||
eventTileOps: tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined,
|
||||
collapseReplyThread: replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined,
|
||||
e2eInfoCallback: e2eInfoCallback,
|
||||
onFinished: () => {
|
||||
this.onFocusChange(false);
|
||||
},
|
||||
});
|
||||
|
||||
this.onFocusChange(true);
|
||||
}
|
||||
|
||||
render() {
|
||||
// TODO: Move the bar to a separate element once there are several buttons
|
||||
return <span className="mx_MessageActionBar mx_MessageActionBar_optionsButton"
|
||||
title={_t("Options")}
|
||||
onClick={this.onOptionsClicked}
|
||||
/>;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
|
||||
'use strict';
|
||||
|
||||
|
||||
import ReplyThread from "../elements/ReplyThread";
|
||||
|
||||
const React = require('react');
|
||||
|
@ -30,7 +29,6 @@ const sdk = require('../../../index');
|
|||
const TextForEvent = require('../../../TextForEvent');
|
||||
import withMatrixClient from '../../../wrappers/withMatrixClient';
|
||||
|
||||
const ContextualMenu = require('../../structures/ContextualMenu');
|
||||
import dis from '../../../dispatcher';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {EventStatus} from 'matrix-js-sdk';
|
||||
|
@ -172,8 +170,8 @@ module.exports = withMatrixClient(React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
// Whether the context menu is being displayed.
|
||||
menu: false,
|
||||
// Whether the action bar is focused.
|
||||
actionBarFocused: false,
|
||||
// Whether all read receipts are being displayed. If not, only display
|
||||
// a truncation of them.
|
||||
allReadAvatars: false,
|
||||
|
@ -309,36 +307,6 @@ module.exports = withMatrixClient(React.createClass({
|
|||
return actions.tweaks.highlight;
|
||||
},
|
||||
|
||||
onOptionsClicked: function(e) {
|
||||
const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
|
||||
const buttonRect = e.target.getBoundingClientRect();
|
||||
|
||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||
const x = buttonRect.right + window.pageXOffset;
|
||||
const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
|
||||
const self = this;
|
||||
|
||||
const {tile, replyThread} = this.refs;
|
||||
|
||||
let e2eInfoCallback = null;
|
||||
if (this.props.mxEvent.isEncrypted()) e2eInfoCallback = () => this.onCryptoClicked();
|
||||
|
||||
ContextualMenu.createMenu(MessageContextMenu, {
|
||||
chevronOffset: 10,
|
||||
mxEvent: this.props.mxEvent,
|
||||
left: x,
|
||||
top: y,
|
||||
permalinkCreator: this.props.permalinkCreator,
|
||||
eventTileOps: tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined,
|
||||
collapseReplyThread: replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined,
|
||||
e2eInfoCallback: e2eInfoCallback,
|
||||
onFinished: function() {
|
||||
self.setState({menu: false});
|
||||
},
|
||||
});
|
||||
this.setState({menu: true});
|
||||
},
|
||||
|
||||
toggleAllReadAvatars: function() {
|
||||
this.setState({
|
||||
allReadAvatars: !this.state.allReadAvatars,
|
||||
|
@ -490,6 +458,12 @@ module.exports = withMatrixClient(React.createClass({
|
|||
return null;
|
||||
},
|
||||
|
||||
onActionBarFocusChange(focused) {
|
||||
this.setState({
|
||||
actionBarFocused: focused,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
|
||||
const SenderProfile = sdk.getComponent('messages.SenderProfile');
|
||||
|
@ -536,7 +510,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
mx_EventTile_continuation: this.props.tileShape ? '' : this.props.continuation,
|
||||
mx_EventTile_last: this.props.last,
|
||||
mx_EventTile_contextual: this.props.contextual,
|
||||
menu: this.state.menu,
|
||||
mx_EventTile_actionBarFocused: this.state.actionBarFocused,
|
||||
mx_EventTile_verified: this.state.verified === true,
|
||||
mx_EventTile_unverified: this.state.verified === false,
|
||||
mx_EventTile_bad: isEncryptionFailure,
|
||||
|
@ -602,9 +576,14 @@ module.exports = withMatrixClient(React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
const optionsButton = (
|
||||
<span className="mx_EventTile_optionsButton" title={_t("Options")} onClick={this.onOptionsClicked} />
|
||||
);
|
||||
const MessageActionBar = sdk.getComponent('messages.MessageActionBar');
|
||||
const actionBar = <MessageActionBar
|
||||
mxEvent={this.props.mxEvent}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
tile={this.refs.tile}
|
||||
replyThread={this.refs.replyThread}
|
||||
onFocusChange={this.onActionBarFocusChange}
|
||||
/>;
|
||||
|
||||
const timestamp = this.props.mxEvent.getTs() ?
|
||||
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null;
|
||||
|
@ -755,7 +734,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
showUrlPreview={this.props.showUrlPreview}
|
||||
onHeightChanged={this.props.onHeightChanged} />
|
||||
{ keyRequestInfo }
|
||||
{ optionsButton }
|
||||
{ actionBar }
|
||||
</div>
|
||||
{
|
||||
// The avatar goes after the event tile as it's absolutly positioned to be over the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue