From 5b8c51e2c21f7725b73c01471a8ccf29efe8ab14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 13 Dec 2020 08:31:40 +0100 Subject: [PATCH 1/5] Basic right click menu --- src/components/views/rooms/EventTile.js | 65 +++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 11277daa573..5abc75bc1ea 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -37,6 +37,7 @@ import {E2E_STATE} from "./E2EIcon"; import {toRem} from "../../../utils/units"; import {WidgetType} from "../../../widgets/WidgetType"; import RoomAvatar from "../avatars/RoomAvatar"; +import {ContextMenu, ChevronFace} from '../../structures/ContextMenu'; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', @@ -230,6 +231,9 @@ export default class EventTile extends React.Component { // whether or not to show flair at all enableFlair: PropTypes.bool, + + /* position of the context menu */ + contextMenuPosition: PropTypes.object, }; static defaultProps = { @@ -254,6 +258,8 @@ export default class EventTile extends React.Component { previouslyRequestedKeys: false, // The Relations model from the JS SDK for reactions to `mxEvent` reactions: this.getReactions(), + + contextMenuPosition: null, }; // don't do RR animations until we are mounted @@ -632,6 +638,54 @@ export default class EventTile extends React.Component { }); }; + renderMenu() { + let contextMenu = null; + if (this.state.contextMenuPosition) { + const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu'); + + const tile = this.getTile && this.getTile(); + const replyThread = this.getReplyThread && this.getReplyThread(); + + contextMenu = ( + + + + ); + } + + return ( + + { contextMenu } + + ); + } + + onContextMenu = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + this.setState({ + contextMenuPosition: { + left: ev.clientX, + top: ev.clientY, + height: 0, + }, + }); + }; + + onCloseMenu = () => { + this.setState({contextMenuPosition: null}); + } + render() { const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp'); const SenderProfile = sdk.getComponent('messages.SenderProfile'); @@ -852,7 +906,8 @@ export default class EventTile extends React.Component { case 'notif': { const room = this.context.getRoom(this.props.mxEvent.getRoomId()); return ( -
+
+ {this.renderMenu()}
@@ -879,7 +934,8 @@ export default class EventTile extends React.Component { } case 'file_grid': { return ( -
+
+ {this.renderMenu()}
+
{ ircTimestamp } { avatar } { sender } @@ -946,7 +1002,8 @@ export default class EventTile extends React.Component { // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return ( -
+
+ {this.renderMenu()} { ircTimestamp }
{ readAvatars } From 083139e3962e5a4f12ade8de8986166eabbb5dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 13 Dec 2020 08:38:16 +0100 Subject: [PATCH 2/5] Remove nonsense line --- src/components/views/rooms/EventTile.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 5abc75bc1ea..fab35c1e6bb 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -231,9 +231,6 @@ export default class EventTile extends React.Component { // whether or not to show flair at all enableFlair: PropTypes.bool, - - /* position of the context menu */ - contextMenuPosition: PropTypes.object, }; static defaultProps = { From 1550cf5849b0c7210882c426adcdd4cf67565d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 14 Dec 2020 17:01:18 +0100 Subject: [PATCH 3/5] Added react, reply and edit buttons --- .../views/context_menus/MessageContextMenu.js | 79 ++++++++++++++++++- src/components/views/rooms/EventTile.js | 1 + 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index bc4514f8a6f..c6c8a5a2ee4 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -29,9 +29,12 @@ import Modal from '../../../Modal'; import Resend from '../../../Resend'; import SettingsStore from '../../../settings/SettingsStore'; import { isUrlPermitted } from '../../../HtmlUtils'; -import { isContentActionable } from '../../../utils/EventUtils'; +import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; import {MenuItem} from "../../structures/ContextMenu"; import {EventType} from "matrix-js-sdk/src/@types/event"; +import {useRovingTabIndex} from "../../../accessibility/RovingTabIndex"; +import {aboveLeftOf, ContextMenu, useContextMenu} from '../../structures/ContextMenu'; +import RoomContext from "../../../contexts/RoomContext"; function canCancel(eventStatus) { return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT; @@ -50,6 +53,9 @@ export default class MessageContextMenu extends React.Component { /* callback called when the menu is dismissed */ onFinished: PropTypes.func, + + /* if true show react button */ + rightClick: PropTypes.bool, }; state = { @@ -57,6 +63,8 @@ export default class MessageContextMenu extends React.Component { canPin: false, }; + static contextType = RoomContext; + componentDidMount() { MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions); this._checkPermissions(); @@ -286,6 +294,22 @@ export default class MessageContextMenu extends React.Component { return this._getReactions(e => e.status === EventStatus.NOT_SENT); } + onEditClick = (ev) => { + dis.dispatch({ + action: 'edit_event', + event: this.props.mxEvent, + }); + this.closeMenu(); + }; + + onReplyClick = (ev) => { + dis.dispatch({ + action: 'reply_to_event', + event: this.props.mxEvent, + }); + this.closeMenu(); + }; + render() { const cli = MatrixClientPeg.get(); const me = cli.getUserId(); @@ -312,6 +336,9 @@ export default class MessageContextMenu extends React.Component { let externalURLButton; let quoteButton; let collapseReplyThread; + let reactButton; + let replyButton; + let editButton; // status is SENT before remote-echo, null after const isSent = !eventStatus || eventStatus === EventStatus.SENT; @@ -468,8 +495,32 @@ export default class MessageContextMenu extends React.Component { ); } + if (this.props.rightClick) { + if (isContentActionable(this.props.mxEvent)) { + if (this.context.canReact) { + reactButton = ; + } + if (this.context.canReply) { + replyButton = + {_t("Reply")} + ; + } + } + if (canEditContent(this.props.mxEvent)) { + editButton = + {_t("Edit")} + ; + } + } + return (
+ { reactButton } + { replyButton } + { editButton } { resendButton } { resendEditButton } { resendReactionsButton } @@ -490,3 +541,29 @@ export default class MessageContextMenu extends React.Component { ); } } + +const ReactButton = ({mxEvent, onCloseContextMenu}) => { + const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); + const ref = useRovingTabIndex(button)[2]; + + const closeReactionPicker = () => { + closeMenu(); + onCloseContextMenu(); + }; + + let reactionPicker; + if (menuDisplayed) { + const buttonRect = button.current.getBoundingClientRect(); + const ReactionPicker = sdk.getComponent('emojipicker.ReactionPicker'); + reactionPicker = + + ; + } + + return + + {_t("React")} + + { reactionPicker } + ; +}; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index fab35c1e6bb..fda0f4c8984 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -655,6 +655,7 @@ export default class EventTile extends React.Component { eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined} collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined} onFinished={this.onCloseMenu} + rightClick={true} /> ); From f863341b6c72464fde47eae0e7294d3a62efc868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 14 Dec 2020 17:21:25 +0100 Subject: [PATCH 4/5] Fix weird behaviour --- src/components/views/rooms/EventTile.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index fda0f4c8984..2713fb28571 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -904,7 +904,7 @@ export default class EventTile extends React.Component { case 'notif': { const room = this.context.getRoom(this.props.mxEvent.getRoomId()); return ( -
+
{this.renderMenu()}
@@ -919,7 +919,7 @@ export default class EventTile extends React.Component { { timestamp }
-
+
+
{this.renderMenu()} -
+
+
{ ircTimestamp } { avatar } { sender } { ircPadlock } -
+
{ groupTimestamp } { groupPadlock } { thread } @@ -1000,7 +1000,7 @@ export default class EventTile extends React.Component { // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return ( -
+
{this.renderMenu()} { ircTimestamp }
@@ -1008,7 +1008,7 @@ export default class EventTile extends React.Component {
{ sender } { ircPadlock } -
+
{ groupTimestamp } { groupPadlock } { thread } From 3c148dca94de13222a5e4da5c21d1a1806257f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 14 Dec 2020 17:48:59 +0100 Subject: [PATCH 5/5] Fix long line --- src/components/views/rooms/EventTile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 2713fb28571..8bac7fdd169 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -642,6 +642,7 @@ export default class EventTile extends React.Component { const tile = this.getTile && this.getTile(); const replyThread = this.getReplyThread && this.getReplyThread(); + const collapseReplyThread = replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined; contextMenu = (