migrate the message context menu to IconizedContextMenu

Signed-off-by: Michael Weimann <mail@michael-weimann.eu>
This commit is contained in:
Michael Weimann 2021-02-21 18:02:34 +01:00
parent 8f6d31b73c
commit e4eeafd485
No known key found for this signature in database
GPG key ID: 34F0524D4DA694A1
4 changed files with 124 additions and 96 deletions

View file

@ -57,7 +57,6 @@
@import "./views/beta/_BetaCard.scss"; @import "./views/beta/_BetaCard.scss";
@import "./views/context_menus/_CallContextMenu.scss"; @import "./views/context_menus/_CallContextMenu.scss";
@import "./views/context_menus/_IconizedContextMenu.scss"; @import "./views/context_menus/_IconizedContextMenu.scss";
@import "./views/context_menus/_MessageContextMenu.scss";
@import "./views/context_menus/_StatusMessageContextMenu.scss"; @import "./views/context_menus/_StatusMessageContextMenu.scss";
@import "./views/context_menus/_TagTileContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss";
@import "./views/dialogs/_AddExistingToSpaceDialog.scss"; @import "./views/dialogs/_AddExistingToSpaceDialog.scss";

View file

@ -1,30 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket 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.
*/
.mx_MessageContextMenu {
padding: 6px;
}
.mx_MessageContextMenu_field {
display: block;
padding: 3px 6px 3px 6px;
cursor: pointer;
white-space: nowrap;
}
.mx_MessageContextMenu_field.mx_MessageContextMenu_fieldSet {
font-weight: bold;
}

View file

@ -22,13 +22,13 @@ import { EventStatus } from 'matrix-js-sdk/src/models/event';
import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../MatrixClientPeg';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import {_t} from '../../../languageHandler';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import Resend from '../../../Resend'; 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 { MenuItem } from "../../structures/ContextMenu"; import IconizedContextMenu, {IconizedContextMenuOption, IconizedContextMenuOptionList} from "./IconizedContextMenu";
import { EventType } from "matrix-js-sdk/src/@types/event"; import { EventType } from "matrix-js-sdk/src/@types/event";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard"; import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard";
@ -257,55 +257,62 @@ export default class MessageContextMenu extends React.Component {
let externalURLButton; let externalURLButton;
let quoteButton; let quoteButton;
let collapseReplyThread; let collapseReplyThread;
const optionLists = [];
// status is SENT before remote-echo, null after // status is SENT before remote-echo, null after
const isSent = !eventStatus || eventStatus === EventStatus.SENT; const isSent = !eventStatus || eventStatus === EventStatus.SENT;
if (!mxEvent.isRedacted()) { if (!mxEvent.isRedacted()) {
if (unsentReactionsCount !== 0) { if (unsentReactionsCount !== 0) {
resendReactionsButton = ( resendReactionsButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onResendReactionsClick}> <IconizedContextMenuOption
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) } label={ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
</MenuItem> onClick={this.onResendReactionsClick}
/>
); );
} }
} }
if (isSent && this.state.canRedact) { if (isSent && this.state.canRedact) {
redactButton = ( redactButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onRedactClick}> <IconizedContextMenuOption
{ _t('Remove') } label={_t("Remove")}
</MenuItem> onClick={this.onRedactClick}
/>
); );
} }
if (isContentActionable(mxEvent)) { if (isContentActionable(mxEvent)) {
forwardButton = ( forwardButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onForwardClick}> <IconizedContextMenuOption
{ _t('Forward Message') } label={_t("Forward Message")}
</MenuItem> onClick={this.onForwardClick}
/>
); );
if (this.state.canPin) { if (this.state.canPin) {
pinButton = ( pinButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onPinClick}> <IconizedContextMenuOption
{ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') } label={ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
</MenuItem> onClick={this.onPinClick}
/>
); );
} }
} }
const viewSourceButton = ( const viewSourceButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}> <IconizedContextMenuOption
{ _t('View Source') } label={_t("View Source")}
</MenuItem> onClick={this.onViewSourceClick}
/>
); );
if (this.props.eventTileOps) { if (this.props.eventTileOps) {
if (this.props.eventTileOps.isWidgetHidden()) { if (this.props.eventTileOps.isWidgetHidden()) {
unhidePreviewButton = ( unhidePreviewButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}> <IconizedContextMenuOption
{ _t('Unhide Preview') } label={_t("Unhide Preview")}
</MenuItem> onClick={this.onUnhidePreviewClick}
/>
); );
} }
} }
@ -316,77 +323,130 @@ export default class MessageContextMenu extends React.Component {
} }
// 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 = (
<MenuItem <IconizedContextMenuOption
element="a"
className="mx_MessageContextMenu_field"
onClick={this.onPermalinkClick} onClick={this.onPermalinkClick}
label= { mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
? _t('Share Permalink') : _t('Share Message') }
element="a"
href={permalink} href={permalink}
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
> />
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
? _t('Share Permalink') : _t('Share Message') }
</MenuItem>
); );
if (this.props.eventTileOps) { // this event is rendered using TextualBody if (this.props.eventTileOps) { // this event is rendered using TextualBody
quoteButton = ( quoteButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onQuoteClick}> <IconizedContextMenuOption
{ _t('Quote') } label={_t("Quote")}
</MenuItem> onClick={this.onQuoteClick}
/>
); );
} }
// Bridges can provide a 'external_url' to link back to the source. // Bridges can provide a 'external_url' to link back to the source.
if ( if (
typeof(mxEvent.event.content.external_url) === "string" && typeof (mxEvent.event.content.external_url) === "string" &&
isUrlPermitted(mxEvent.event.content.external_url) isUrlPermitted(mxEvent.event.content.external_url)
) { ) {
externalURLButton = ( externalURLButton = (
<MenuItem <IconizedContextMenuOption
onClick={this.closeMenu}
label={ _t('Source URL') }
element="a" element="a"
className="mx_MessageContextMenu_field"
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
onClick={this.closeMenu}
href={mxEvent.event.content.external_url} href={mxEvent.event.content.external_url}
> />
{ _t('Source URL') }
</MenuItem>
); );
} }
if (this.props.collapseReplyThread) { if (this.props.collapseReplyThread) {
collapseReplyThread = ( collapseReplyThread = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onCollapseReplyThreadClick}> <IconizedContextMenuOption
{ _t('Collapse Reply Thread') } label={_t("Collapse Reply Thread")}
</MenuItem> onClick={this.onCollapseReplyThreadClick}
/>
); );
} }
let reportEventButton; let reportEventButton;
if (mxEvent.getSender() !== me) { if (mxEvent.getSender() !== me) {
reportEventButton = ( reportEventButton = (
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onReportEventClick}> <IconizedContextMenuOption
{ _t('Report Content') } label={_t("Report Content")}
</MenuItem> onClick={this.onReportEventClick}
/>
); );
} }
if (forwardButton || quoteButton || collapseReplyThread) {
optionLists.push((
<IconizedContextMenuOptionList>
{quoteButton}
{forwardButton}
{collapseReplyThread}
</IconizedContextMenuOptionList>
));
}
if (resendReactionsButton) {
optionLists.push((
<IconizedContextMenuOptionList>
{resendReactionsButton}
</IconizedContextMenuOptionList>
));
}
if (externalURLButton || permalinkButton) {
optionLists.push((
<IconizedContextMenuOptionList>
{externalURLButton}
{permalinkButton}
</IconizedContextMenuOptionList>
));
}
if (pinButton || unhidePreviewButton) {
optionLists.push((
<IconizedContextMenuOptionList>
{pinButton}
{unhidePreviewButton}
</IconizedContextMenuOptionList>
));
}
if (reportEventButton) {
optionLists.push((
<IconizedContextMenuOptionList>
{reportEventButton}
</IconizedContextMenuOptionList>
));
}
if (viewSourceButton) {
optionLists.push((
<IconizedContextMenuOptionList>
{viewSourceButton}
</IconizedContextMenuOptionList>
));
}
if (redactButton) {
optionLists.push((
<IconizedContextMenuOptionList red>
{redactButton}
</IconizedContextMenuOptionList>
));
}
return ( return (
<div className="mx_MessageContextMenu"> <IconizedContextMenu
{ resendReactionsButton } {...this.props}
{ redactButton } className="mx_MessageContextMenu"
{ forwardButton } compact={true}
{ pinButton } >
{ viewSourceButton } {optionLists}
{ unhidePreviewButton } </IconizedContextMenu>
{ permalinkButton }
{ quoteButton }
{ externalURLButton }
{ collapseReplyThread }
{ reportEventButton }
</div>
); );
} }
} }

View file

@ -48,15 +48,14 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo
const replyThread = getReplyThread && getReplyThread(); const replyThread = getReplyThread && getReplyThread();
const buttonRect = button.current.getBoundingClientRect(); const buttonRect = button.current.getBoundingClientRect();
contextMenu = <ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu}> contextMenu = <MessageContextMenu
<MessageContextMenu {...aboveLeftOf(buttonRect)}
mxEvent={mxEvent} mxEvent={mxEvent}
permalinkCreator={permalinkCreator} permalinkCreator={permalinkCreator}
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined} eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined} collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
onFinished={closeMenu} onFinished={closeMenu}
/> />;
</ContextMenu>;
} }
return <React.Fragment> return <React.Fragment>