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:
J. Ryan Stinnett 2019-04-26 12:14:30 +01:00
parent 530c92e03d
commit ed8bbc7082
6 changed files with 148 additions and 61 deletions

View 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}
/>;
}
}

View file

@ -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