mirror of https://github.com/vector-im/riot-web
Cleanup `MessageContextMenu` code (#8351)
parent
2f3249793f
commit
511965b840
|
@ -21,7 +21,6 @@ import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
|
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { Relations } from 'matrix-js-sdk/src/models/relations';
|
import { Relations } from 'matrix-js-sdk/src/models/relations';
|
||||||
import { RoomMemberEvent } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMemberEvent } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { M_LOCATION } from 'matrix-js-sdk/src/@types/location';
|
|
||||||
import { M_POLL_START } from "matrix-events-sdk";
|
import { M_POLL_START } from "matrix-events-sdk";
|
||||||
|
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
|
@ -31,7 +30,7 @@ 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 { canEditContent, editEvent, isContentActionable } from '../../../utils/EventUtils';
|
import { canEditContent, canForward, editEvent, isContentActionable, isLocationEvent } from '../../../utils/EventUtils';
|
||||||
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu';
|
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu';
|
||||||
import { ReadPinsEventId } from "../right_panel/types";
|
import { ReadPinsEventId } from "../right_panel/types";
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
|
@ -49,20 +48,11 @@ import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInse
|
||||||
import EndPollDialog from '../dialogs/EndPollDialog';
|
import EndPollDialog from '../dialogs/EndPollDialog';
|
||||||
import { isPollEnded } from '../messages/MPollBody';
|
import { isPollEnded } from '../messages/MPollBody';
|
||||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
import { GetRelationsForEvent } from "../rooms/EventTile";
|
import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile";
|
||||||
import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload";
|
import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload";
|
||||||
import { OpenReportEventDialogPayload } from "../../../dispatcher/payloads/OpenReportEventDialogPayload";
|
import { OpenReportEventDialogPayload } from "../../../dispatcher/payloads/OpenReportEventDialogPayload";
|
||||||
import { createMapSiteLink } from '../../../utils/location';
|
import { createMapSiteLink } from '../../../utils/location';
|
||||||
|
|
||||||
export interface IEventTileOps {
|
|
||||||
isWidgetHidden(): boolean;
|
|
||||||
unhideWidget(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IOperableEventTile {
|
|
||||||
getEventTileOps(): IEventTileOps;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends IPosition {
|
interface IProps extends IPosition {
|
||||||
chevronFace: ChevronFace;
|
chevronFace: ChevronFace;
|
||||||
/* the MatrixEvent associated with the context menu */
|
/* the MatrixEvent associated with the context menu */
|
||||||
|
@ -343,28 +333,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
const isThreadRootEvent = isThread && mxEvent?.getThread()?.rootEvent === mxEvent;
|
const isThreadRootEvent = isThread && mxEvent?.getThread()?.rootEvent === mxEvent;
|
||||||
|
|
||||||
let openInMapSiteButton: JSX.Element;
|
|
||||||
let endPollButton: JSX.Element;
|
|
||||||
let resendReactionsButton: JSX.Element;
|
let resendReactionsButton: JSX.Element;
|
||||||
let redactButton: JSX.Element;
|
|
||||||
let forwardButton: JSX.Element;
|
|
||||||
let pinButton: JSX.Element;
|
|
||||||
let unhidePreviewButton: JSX.Element;
|
|
||||||
let externalURLButton: JSX.Element;
|
|
||||||
let quoteButton: JSX.Element;
|
|
||||||
let redactItemList: JSX.Element;
|
|
||||||
let reportEventButton: JSX.Element;
|
|
||||||
let copyButton: JSX.Element;
|
|
||||||
let editButton: JSX.Element;
|
|
||||||
let replyButton: JSX.Element;
|
|
||||||
let reactButton: JSX.Element;
|
|
||||||
let reactionPicker: JSX.Element;
|
|
||||||
let quickItemsList: JSX.Element;
|
|
||||||
let nativeItemsList: JSX.Element;
|
|
||||||
let permalinkButton: JSX.Element;
|
|
||||||
let collapseReplyChainButton: JSX.Element;
|
|
||||||
let viewInRoomButton: JSX.Element;
|
|
||||||
|
|
||||||
if (!mxEvent.isRedacted() && unsentReactionsCount !== 0) {
|
if (!mxEvent.isRedacted() && unsentReactionsCount !== 0) {
|
||||||
resendReactionsButton = (
|
resendReactionsButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -375,6 +344,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let redactButton: JSX.Element;
|
||||||
if (isSent && this.state.canRedact) {
|
if (isSent && this.state.canRedact) {
|
||||||
redactButton = (
|
redactButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -385,6 +355,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let openInMapSiteButton: JSX.Element;
|
||||||
if (this.canOpenInMapSite(mxEvent)) {
|
if (this.canOpenInMapSite(mxEvent)) {
|
||||||
const mapSiteLink = createMapSiteLink(mxEvent);
|
const mapSiteLink = createMapSiteLink(mxEvent);
|
||||||
openInMapSiteButton = (
|
openInMapSiteButton = (
|
||||||
|
@ -404,6 +375,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let forwardButton: JSX.Element;
|
||||||
if (contentActionable && canForward(mxEvent)) {
|
if (contentActionable && canForward(mxEvent)) {
|
||||||
forwardButton = (
|
forwardButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -414,6 +386,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pinButton: JSX.Element;
|
||||||
if (contentActionable && this.state.canPin) {
|
if (contentActionable && this.state.canPin) {
|
||||||
pinButton = (
|
pinButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -435,6 +408,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unhidePreviewButton: JSX.Element;
|
||||||
if (eventTileOps?.isWidgetHidden()) {
|
if (eventTileOps?.isWidgetHidden()) {
|
||||||
unhidePreviewButton = (
|
unhidePreviewButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -445,6 +419,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let permalinkButton: JSX.Element;
|
||||||
if (permalink) {
|
if (permalink) {
|
||||||
permalinkButton = (
|
permalinkButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -468,6 +443,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let endPollButton: JSX.Element;
|
||||||
if (this.canEndPoll(mxEvent)) {
|
if (this.canEndPoll(mxEvent)) {
|
||||||
endPollButton = (
|
endPollButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -478,6 +454,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let quoteButton: JSX.Element;
|
||||||
if (eventTileOps) { // this event is rendered using TextualBody
|
if (eventTileOps) { // this event is rendered using TextualBody
|
||||||
quoteButton = (
|
quoteButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -489,6 +466,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bridges can provide a 'external_url' to link back to the source.
|
// Bridges can provide a 'external_url' to link back to the source.
|
||||||
|
let externalURLButton: JSX.Element;
|
||||||
if (
|
if (
|
||||||
typeof (mxEvent.getContent().external_url) === "string" &&
|
typeof (mxEvent.getContent().external_url) === "string" &&
|
||||||
isUrlPermitted(mxEvent.getContent().external_url)
|
isUrlPermitted(mxEvent.getContent().external_url)
|
||||||
|
@ -511,6 +489,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let collapseReplyChainButton: JSX.Element;
|
||||||
if (collapseReplyChain) {
|
if (collapseReplyChain) {
|
||||||
collapseReplyChainButton = (
|
collapseReplyChainButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -521,6 +500,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reportEventButton: JSX.Element;
|
||||||
if (mxEvent.getSender() !== me) {
|
if (mxEvent.getSender() !== me) {
|
||||||
reportEventButton = (
|
reportEventButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -531,6 +511,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let copyButton: JSX.Element;
|
||||||
if (rightClick && getSelectedText()) {
|
if (rightClick && getSelectedText()) {
|
||||||
copyButton = (
|
copyButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -542,6 +523,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let editButton: JSX.Element;
|
||||||
if (rightClick && canEditContent(mxEvent)) {
|
if (rightClick && canEditContent(mxEvent)) {
|
||||||
editButton = (
|
editButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -552,6 +534,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let replyButton: JSX.Element;
|
||||||
if (rightClick && contentActionable && canSendMessages) {
|
if (rightClick && contentActionable && canSendMessages) {
|
||||||
replyButton = (
|
replyButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -562,6 +545,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reactButton;
|
||||||
if (rightClick && contentActionable && canReact) {
|
if (rightClick && contentActionable && canReact) {
|
||||||
reactButton = (
|
reactButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -573,6 +557,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let viewInRoomButton: JSX.Element;
|
||||||
if (isThreadRootEvent) {
|
if (isThreadRootEvent) {
|
||||||
viewInRoomButton = (
|
viewInRoomButton = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -583,6 +568,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nativeItemsList: JSX.Element;
|
||||||
if (copyButton) {
|
if (copyButton) {
|
||||||
nativeItemsList = (
|
nativeItemsList = (
|
||||||
<IconizedContextMenuOptionList>
|
<IconizedContextMenuOptionList>
|
||||||
|
@ -591,6 +577,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let quickItemsList: JSX.Element;
|
||||||
if (editButton || replyButton || reactButton) {
|
if (editButton || replyButton || reactButton) {
|
||||||
quickItemsList = (
|
quickItemsList = (
|
||||||
<IconizedContextMenuOptionList>
|
<IconizedContextMenuOptionList>
|
||||||
|
@ -619,6 +606,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
</IconizedContextMenuOptionList>
|
</IconizedContextMenuOptionList>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let redactItemList: JSX.Element;
|
||||||
if (redactButton) {
|
if (redactButton) {
|
||||||
redactItemList = (
|
redactItemList = (
|
||||||
<IconizedContextMenuOptionList red>
|
<IconizedContextMenuOptionList red>
|
||||||
|
@ -627,6 +615,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reactionPicker: JSX.Element;
|
||||||
if (this.state.reactionPickerDisplayed) {
|
if (this.state.reactionPickerDisplayed) {
|
||||||
const buttonRect = (this.reactButtonRef.current as HTMLElement)?.getBoundingClientRect();
|
const buttonRect = (this.reactButtonRef.current as HTMLElement)?.getBoundingClientRect();
|
||||||
reactionPicker = (
|
reactionPicker = (
|
||||||
|
@ -662,20 +651,3 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function canForward(event: MatrixEvent): boolean {
|
|
||||||
return !(
|
|
||||||
isLocationEvent(event) ||
|
|
||||||
M_POLL_START.matches(event.getType())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLocationEvent(event: MatrixEvent): boolean {
|
|
||||||
const eventType = event.getType();
|
|
||||||
return (
|
|
||||||
M_LOCATION.matches(eventType) ||
|
|
||||||
(
|
|
||||||
eventType === EventType.RoomMessage &&
|
|
||||||
M_LOCATION.matches(event.getContent().msgtype)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ import { Mjolnir } from "../../../mjolnir/Mjolnir";
|
||||||
import RedactedBody from "./RedactedBody";
|
import RedactedBody from "./RedactedBody";
|
||||||
import UnknownBody from "./UnknownBody";
|
import UnknownBody from "./UnknownBody";
|
||||||
import { IMediaBody } from "./IMediaBody";
|
import { IMediaBody } from "./IMediaBody";
|
||||||
import { IOperableEventTile } from "../context_menus/MessageContextMenu";
|
|
||||||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||||
import { ReactAnyComponent } from "../../../@types/common";
|
import { ReactAnyComponent } from "../../../@types/common";
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
|
@ -41,6 +40,7 @@ import MPollBody from "./MPollBody";
|
||||||
import MLocationBody from "./MLocationBody";
|
import MLocationBody from "./MLocationBody";
|
||||||
import MjolnirBody from "./MjolnirBody";
|
import MjolnirBody from "./MjolnirBody";
|
||||||
import MBeaconBody from "./MBeaconBody";
|
import MBeaconBody from "./MBeaconBody";
|
||||||
|
import { IEventTileOps } from "../rooms/EventTile";
|
||||||
|
|
||||||
// onMessageAllowed is handled internally
|
// onMessageAllowed is handled internally
|
||||||
interface IProps extends Omit<IBodyProps, "onMessageAllowed" | "mediaEventHelper"> {
|
interface IProps extends Omit<IBodyProps, "onMessageAllowed" | "mediaEventHelper"> {
|
||||||
|
@ -54,6 +54,10 @@ interface IProps extends Omit<IBodyProps, "onMessageAllowed" | "mediaEventHelper
|
||||||
isSeeingThroughMessageHiddenForModeration?: boolean;
|
isSeeingThroughMessageHiddenForModeration?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IOperableEventTile {
|
||||||
|
getEventTileOps(): IEventTileOps;
|
||||||
|
}
|
||||||
|
|
||||||
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
|
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
|
||||||
private body: React.RefObject<React.Component | IOperableEventTile> = createRef();
|
private body: React.RefObject<React.Component | IOperableEventTile> = createRef();
|
||||||
private mediaHelper: MediaEventHelper;
|
private mediaHelper: MediaEventHelper;
|
||||||
|
|
|
@ -38,7 +38,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { E2EState } from "./E2EIcon";
|
import { E2EState } from "./E2EIcon";
|
||||||
import { toRem } from "../../../utils/units";
|
import { toRem } from "../../../utils/units";
|
||||||
import RoomAvatar from "../avatars/RoomAvatar";
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
import MessageContextMenu, { IEventTileOps } from "../context_menus/MessageContextMenu";
|
import MessageContextMenu from "../context_menus/MessageContextMenu";
|
||||||
import { aboveRightOf } from '../../structures/ContextMenu';
|
import { aboveRightOf } from '../../structures/ContextMenu';
|
||||||
import { objectHasDiff } from "../../../utils/objects";
|
import { objectHasDiff } from "../../../utils/objects";
|
||||||
import Tooltip from "../elements/Tooltip";
|
import Tooltip from "../elements/Tooltip";
|
||||||
|
@ -99,6 +99,11 @@ export interface IReadReceiptProps {
|
||||||
ts: number;
|
ts: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEventTileOps {
|
||||||
|
isWidgetHidden(): boolean;
|
||||||
|
unhideWidget(): void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IEventTileType extends React.Component {
|
export interface IEventTileType extends React.Component {
|
||||||
getEventTileOps?(): IEventTileOps;
|
getEventTileOps?(): IEventTileOps;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { EventType, EVENT_VISIBILITY_CHANGE_TYPE, MsgType, RelationType } from "
|
||||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||||
import { logger } from 'matrix-js-sdk/src/logger';
|
import { logger } from 'matrix-js-sdk/src/logger';
|
||||||
import { M_POLL_START } from "matrix-events-sdk";
|
import { M_POLL_START } from "matrix-events-sdk";
|
||||||
|
import { M_LOCATION } from "matrix-js-sdk/src/@types/location";
|
||||||
|
|
||||||
import { MatrixClientPeg } from '../MatrixClientPeg';
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
import shouldHideEvent from "../shouldHideEvent";
|
import shouldHideEvent from "../shouldHideEvent";
|
||||||
|
@ -262,3 +263,21 @@ export function editEvent(
|
||||||
export function canCancel(status: EventStatus): boolean {
|
export function canCancel(status: EventStatus): boolean {
|
||||||
return status === EventStatus.QUEUED || status === EventStatus.NOT_SENT || status === EventStatus.ENCRYPTING;
|
return status === EventStatus.QUEUED || status === EventStatus.NOT_SENT || status === EventStatus.ENCRYPTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isLocationEvent = (event: MatrixEvent): boolean => {
|
||||||
|
const eventType = event.getType();
|
||||||
|
return (
|
||||||
|
M_LOCATION.matches(eventType) ||
|
||||||
|
(
|
||||||
|
eventType === EventType.RoomMessage &&
|
||||||
|
M_LOCATION.matches(event.getContent().msgtype)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function canForward(event: MatrixEvent): boolean {
|
||||||
|
return !(
|
||||||
|
isLocationEvent(event) ||
|
||||||
|
M_POLL_START.matches(event.getType())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue