Enable the message right-click context menu in the browser (#8336)

* Enable the message right-click context menu in the browser

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>

* Move `getSelectedText()` to `strings.ts`

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>

* Move `canCancel()` to `EventUtils.ts`

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2022-04-15 20:12:40 +02:00 committed by GitHub
parent 1afecc474f
commit 11e0a3a8fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 15 deletions

View file

@ -37,7 +37,7 @@ import { ReadPinsEventId } from "../right_panel/types";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import { ButtonEvent } from '../elements/AccessibleButton'; import { ButtonEvent } from '../elements/AccessibleButton';
import { copyPlaintext } from '../../../utils/strings'; import { copyPlaintext, getSelectedText } from '../../../utils/strings';
import ContextMenu, { toRightOf } from '../../structures/ContextMenu'; import ContextMenu, { toRightOf } from '../../structures/ContextMenu';
import ReactionPicker from '../emojipicker/ReactionPicker'; import ReactionPicker from '../emojipicker/ReactionPicker';
import ViewSource from '../../structures/ViewSource'; import ViewSource from '../../structures/ViewSource';
@ -54,10 +54,6 @@ import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwa
import { OpenReportEventDialogPayload } from "../../../dispatcher/payloads/OpenReportEventDialogPayload"; import { OpenReportEventDialogPayload } from "../../../dispatcher/payloads/OpenReportEventDialogPayload";
import { createMapSiteLink } from '../../../utils/location'; import { createMapSiteLink } from '../../../utils/location';
export function canCancel(status: EventStatus): boolean {
return status === EventStatus.QUEUED || status === EventStatus.NOT_SENT || status === EventStatus.ENCRYPTING;
}
export interface IEventTileOps { export interface IEventTileOps {
isWidgetHidden(): boolean; isWidgetHidden(): boolean;
unhideWidget(): void; unhideWidget(): void;
@ -263,7 +259,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
}; };
private onCopyClick = (): void => { private onCopyClick = (): void => {
copyPlaintext(this.getSelectedText()); copyPlaintext(getSelectedText());
this.closeMenu(); this.closeMenu();
}; };
@ -310,10 +306,6 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
}); });
} }
private getSelectedText(): string {
return window.getSelection().toString();
}
private getPermalink(): string { private getPermalink(): string {
if (!this.props.permalinkCreator) return; if (!this.props.permalinkCreator) return;
return this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()); return this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
@ -539,7 +531,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
); );
} }
if (rightClick && this.getSelectedText()) { if (rightClick && getSelectedText()) {
copyButton = ( copyButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconCopy" iconClassName="mx_MessageContextMenu_iconCopy"

View file

@ -26,11 +26,11 @@ import type { Relations } from 'matrix-js-sdk/src/models/relations';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import ContextMenu, { aboveLeftOf, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu'; import ContextMenu, { aboveLeftOf, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu';
import { isContentActionable, canEditContent, editEvent } from '../../../utils/EventUtils'; import { isContentActionable, canEditContent, editEvent, canCancel } from '../../../utils/EventUtils';
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext"; import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import Toolbar from "../../../accessibility/Toolbar"; import Toolbar from "../../../accessibility/Toolbar";
import { RovingAccessibleTooltipButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex"; import { RovingAccessibleTooltipButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
import MessageContextMenu, { canCancel } from "../context_menus/MessageContextMenu"; import MessageContextMenu from "../context_menus/MessageContextMenu";
import Resend from "../../../Resend"; import Resend from "../../../Resend";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import { MediaEventHelper } from "../../../utils/MediaEventHelper";

View file

@ -70,7 +70,7 @@ import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNo
import { NotificationStateEvents } from '../../../stores/notifications/NotificationState'; import { NotificationStateEvents } from '../../../stores/notifications/NotificationState';
import { NotificationColor } from '../../../stores/notifications/NotificationColor'; import { NotificationColor } from '../../../stores/notifications/NotificationColor';
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton'; import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
import { copyPlaintext } from '../../../utils/strings'; import { copyPlaintext, getSelectedText } from '../../../utils/strings';
import { DecryptionFailureTracker } from '../../../DecryptionFailureTracker'; import { DecryptionFailureTracker } from '../../../DecryptionFailureTracker';
import RedactedBody from '../messages/RedactedBody'; import RedactedBody from '../messages/RedactedBody';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
@ -947,13 +947,26 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
}; };
private showContextMenu(ev: React.MouseEvent, showPermalink?: boolean): void { private showContextMenu(ev: React.MouseEvent, showPermalink?: boolean): void {
// Return if message right-click context menu isn't enabled
if (!SettingsStore.getValue("feature_message_right_click_context_menu")) return; if (!SettingsStore.getValue("feature_message_right_click_context_menu")) return;
// Return if we're in a browser and click either an a tag or we have
// selected text, as in those cases we want to use the native browser
// menu
const clickTarget = ev.target as HTMLElement;
if (
!PlatformPeg.get().allowOverridingNativeContextMenus() &&
(clickTarget.tagName === "a" || clickTarget.closest("a") || getSelectedText())
) return;
// There is no way to copy non-PNG images into clipboard, so we can't // There is no way to copy non-PNG images into clipboard, so we can't
// have our own handling for copying images, so we leave it to the // have our own handling for copying images, so we leave it to the
// Electron layer (webcontents-handler.ts) // Electron layer (webcontents-handler.ts)
if (ev.target instanceof HTMLImageElement) return; if (ev.target instanceof HTMLImageElement) return;
if (!PlatformPeg.get().allowOverridingNativeContextMenus()) return;
// We don't want to show the menu when editing a message
if (this.props.editState) return; if (this.props.editState) return;
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
this.setState({ this.setState({

View file

@ -258,3 +258,7 @@ export function editEvent(
}); });
} }
} }
export function canCancel(status: EventStatus): boolean {
return status === EventStatus.QUEUED || status === EventStatus.NOT_SENT || status === EventStatus.ENCRYPTING;
}

View file

@ -84,3 +84,11 @@ const collator = new Intl.Collator();
export function compare(a: string, b: string): number { export function compare(a: string, b: string): number {
return collator.compare(a, b); return collator.compare(a, b);
} }
/**
* Returns text which has been selected by the user
* @returns the selected text
*/
export function getSelectedText(): string {
return window.getSelection().toString();
}