Make the message context menu more accessible
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
14e3cb8736
commit
8ec0ffea3a
3 changed files with 69 additions and 41 deletions
|
@ -84,9 +84,9 @@ export class TabbedView extends React.Component {
|
||||||
|
|
||||||
const onClickHandler = () => this._setActiveTab(tab);
|
const onClickHandler = () => this._setActiveTab(tab);
|
||||||
const onKeyDownHandler = (e) => {
|
const onKeyDownHandler = (e) => {
|
||||||
|
if (e.keyCode === KeyCode.ENTER || e.keyCode === KeyCode.SPACE) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (e.keyCode === KeyCode.ENTER || e.keyCode === KeyCode.SPACE) {
|
|
||||||
this._setActiveTab(tab);
|
this._setActiveTab(tab);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -30,11 +31,28 @@ import Resend from '../../../Resend';
|
||||||
import SettingsStore from '../../../settings/SettingsStore';
|
import SettingsStore from '../../../settings/SettingsStore';
|
||||||
import { isUrlPermitted } from '../../../HtmlUtils';
|
import { isUrlPermitted } from '../../../HtmlUtils';
|
||||||
import { isContentActionable } from '../../../utils/EventUtils';
|
import { isContentActionable } from '../../../utils/EventUtils';
|
||||||
|
import {KeyCode} from "../../../Keyboard";
|
||||||
|
|
||||||
function canCancel(eventStatus) {
|
function canCancel(eventStatus) {
|
||||||
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DropdownButton = ({children, onClick}) => {
|
||||||
|
const onKeyDown = (e) => {
|
||||||
|
if (e.keyCode === KeyCode.ENTER || e.keyCode === KeyCode.SPACE) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="mx_MessageContextMenu_field" onClick={onClick} onKeyDown={onKeyDown} role="button" tabIndex={0}>
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = createReactClass({
|
module.exports = createReactClass({
|
||||||
displayName: 'MessageContextMenu',
|
displayName: 'MessageContextMenu',
|
||||||
|
|
||||||
|
@ -319,89 +337,89 @@ module.exports = createReactClass({
|
||||||
if (!mxEvent.isRedacted()) {
|
if (!mxEvent.isRedacted()) {
|
||||||
if (eventStatus === EventStatus.NOT_SENT) {
|
if (eventStatus === EventStatus.NOT_SENT) {
|
||||||
resendButton = (
|
resendButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
<DropdownButton onClick={this.onResendClick}>
|
||||||
{ _t('Resend') }
|
{ _t('Resend') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editStatus === EventStatus.NOT_SENT) {
|
if (editStatus === EventStatus.NOT_SENT) {
|
||||||
resendEditButton = (
|
resendEditButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendEditClick}>
|
<DropdownButton onClick={this.onResendEditClick}>
|
||||||
{ _t('Resend edit') }
|
{ _t('Resend edit') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unsentReactionsCount !== 0) {
|
if (unsentReactionsCount !== 0) {
|
||||||
resendReactionsButton = (
|
resendReactionsButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendReactionsClick}>
|
<DropdownButton onClick={this.onResendReactionsClick}>
|
||||||
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
|
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redactStatus === EventStatus.NOT_SENT) {
|
if (redactStatus === EventStatus.NOT_SENT) {
|
||||||
resendRedactionButton = (
|
resendRedactionButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendRedactionClick}>
|
<DropdownButton onClick={this.onResendRedactionClick}>
|
||||||
{ _t('Resend removal') }
|
{ _t('Resend removal') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSent && this.state.canRedact) {
|
if (isSent && this.state.canRedact) {
|
||||||
redactButton = (
|
redactButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
<DropdownButton onClick={this.onRedactClick}>
|
||||||
{ _t('Remove') }
|
{ _t('Remove') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowCancel) {
|
if (allowCancel) {
|
||||||
cancelButton = (
|
cancelButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
<DropdownButton onClick={this.onCancelSendClick}>
|
||||||
{ _t('Cancel Sending') }
|
{ _t('Cancel Sending') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isContentActionable(mxEvent)) {
|
if (isContentActionable(mxEvent)) {
|
||||||
forwardButton = (
|
forwardButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onForwardClick}>
|
<DropdownButton onClick={this.onForwardClick}>
|
||||||
{ _t('Forward Message') }
|
{ _t('Forward Message') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.state.canPin) {
|
if (this.state.canPin) {
|
||||||
pinButton = (
|
pinButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onPinClick}>
|
<DropdownButton onClick={this.onPinClick}>
|
||||||
{ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
|
{ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewSourceButton = (
|
const viewSourceButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}>
|
<DropdownButton onClick={this.onViewSourceClick}>
|
||||||
{ _t('View Source') }
|
{ _t('View Source') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mxEvent.getType() !== mxEvent.getWireType()) {
|
if (mxEvent.getType() !== mxEvent.getWireType()) {
|
||||||
viewClearSourceButton = (
|
viewClearSourceButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewClearSourceClick}>
|
<DropdownButton onClick={this.onViewClearSourceClick}>
|
||||||
{ _t('View Decrypted Source') }
|
{ _t('View Decrypted Source') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.eventTileOps) {
|
if (this.props.eventTileOps) {
|
||||||
if (this.props.eventTileOps.isWidgetHidden()) {
|
if (this.props.eventTileOps.isWidgetHidden()) {
|
||||||
unhidePreviewButton = (
|
unhidePreviewButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}>
|
<DropdownButton onClick={this.onUnhidePreviewClick}>
|
||||||
{ _t('Unhide Preview') }
|
{ _t('Unhide Preview') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,20 +430,19 @@ module.exports = createReactClass({
|
||||||
}
|
}
|
||||||
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
|
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
|
||||||
const permalinkButton = (
|
const permalinkButton = (
|
||||||
<div className="mx_MessageContextMenu_field">
|
<DropdownButton>
|
||||||
<a href={permalink}
|
<a href={permalink} target="_blank" rel="noopener" onClick={this.onPermalinkClick} tabIndex={-1}>
|
||||||
target="_blank" rel="noopener" onClick={this.onPermalinkClick}>
|
|
||||||
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
|
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
|
||||||
? _t('Share Permalink') : _t('Share Message') }
|
? _t('Share Permalink') : _t('Share Message') }
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.props.eventTileOps && this.props.eventTileOps.getInnerText) {
|
if (this.props.eventTileOps && this.props.eventTileOps.getInnerText) {
|
||||||
quoteButton = (
|
quoteButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onQuoteClick}>
|
<DropdownButton onClick={this.onQuoteClick}>
|
||||||
{ _t('Quote') }
|
{ _t('Quote') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,34 +452,43 @@ module.exports = createReactClass({
|
||||||
isUrlPermitted(mxEvent.event.content.external_url)
|
isUrlPermitted(mxEvent.event.content.external_url)
|
||||||
) {
|
) {
|
||||||
externalURLButton = (
|
externalURLButton = (
|
||||||
<div className="mx_MessageContextMenu_field">
|
<DropdownButton>
|
||||||
<a href={mxEvent.event.content.external_url}
|
<a
|
||||||
rel="noopener" target="_blank" onClick={this.closeMenu}>{ _t('Source URL') }</a>
|
href={mxEvent.event.content.external_url}
|
||||||
</div>
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
onClick={this.closeMenu}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
{ _t('Source URL') }
|
||||||
|
</a>
|
||||||
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.collapseReplyThread) {
|
if (this.props.collapseReplyThread) {
|
||||||
collapseReplyThread = (
|
collapseReplyThread = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onCollapseReplyThreadClick}>
|
<DropdownButton onClick={this.onCollapseReplyThreadClick}>
|
||||||
{ _t('Collapse Reply Thread') }
|
{ _t('Collapse Reply Thread') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let e2eInfo;
|
let e2eInfo;
|
||||||
if (this.props.e2eInfoCallback) {
|
if (this.props.e2eInfoCallback) {
|
||||||
e2eInfo = <div className="mx_MessageContextMenu_field" onClick={this.e2eInfoClicked}>
|
e2eInfo = (
|
||||||
|
<DropdownButton onClick={this.e2eInfoClicked}>
|
||||||
{ _t('End-to-end encryption information') }
|
{ _t('End-to-end encryption information') }
|
||||||
</div>;
|
</DropdownButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reportEventButton;
|
let reportEventButton;
|
||||||
if (mxEvent.getSender() !== me) {
|
if (mxEvent.getSender() !== me) {
|
||||||
reportEventButton = (
|
reportEventButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onReportEventClick}>
|
<DropdownButton onClick={this.onReportEventClick}>
|
||||||
{ _t('Report Content') }
|
{ _t('Report Content') }
|
||||||
</div>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,9 +166,11 @@ export default class MessageActionBar extends React.PureComponent {
|
||||||
{reactButton}
|
{reactButton}
|
||||||
{replyButton}
|
{replyButton}
|
||||||
{editButton}
|
{editButton}
|
||||||
<span className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
<span
|
||||||
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
||||||
title={_t("Options")}
|
title={_t("Options")}
|
||||||
onClick={this.onOptionsClick}
|
onClick={this.onOptionsClick}
|
||||||
|
aria-haspopup={true}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue