Skip to content
This repository was archived by the owner on May 18, 2025. It is now read-only.

Commit 26592da

Browse files
authored
Merge pull request matrix-org#5671 from weeman1337/msg-context-menu
2 parents b6d9ecd + d3bc7fe commit 26592da

File tree

11 files changed

+201
-82
lines changed

11 files changed

+201
-82
lines changed
Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
Copyright 2015, 2016 OpenMarket Ltd
3+
Copyright 2021 Michael Weimann <[email protected]>
34
45
Licensed under the Apache License, Version 2.0 (the "License");
56
you may not use this file except in compliance with the License.
@@ -15,16 +16,69 @@ limitations under the License.
1516
*/
1617

1718
.mx_MessageContextMenu {
18-
padding: 6px;
19-
}
2019

21-
.mx_MessageContextMenu_field {
22-
display: block;
23-
padding: 3px 6px 3px 6px;
24-
cursor: pointer;
25-
white-space: nowrap;
26-
}
20+
.mx_IconizedContextMenu_icon {
21+
width: 16px;
22+
height: 16px;
23+
display: block;
24+
25+
&::before {
26+
content: '';
27+
width: 16px;
28+
height: 16px;
29+
display: block;
30+
mask-position: center;
31+
mask-size: contain;
32+
mask-repeat: no-repeat;
33+
background: $primary-fg-color;
34+
}
35+
}
36+
37+
.mx_MessageContextMenu_iconCollapse::before {
38+
mask-image: url('$(res)/img/element-icons/message/chevron-up.svg');
39+
}
40+
41+
.mx_MessageContextMenu_iconReport::before {
42+
mask-image: url('$(res)/img/element-icons/warning-badge.svg');
43+
}
44+
45+
.mx_MessageContextMenu_iconLink::before {
46+
mask-image: url('$(res)/img/element-icons/link.svg');
47+
}
48+
49+
.mx_MessageContextMenu_iconPermalink::before {
50+
mask-image: url('$(res)/img/element-icons/room/share.svg');
51+
}
52+
53+
.mx_MessageContextMenu_iconUnhidePreview::before {
54+
mask-image: url('$(res)/img/element-icons/settings/appearance.svg');
55+
}
56+
57+
.mx_MessageContextMenu_iconForward::before {
58+
mask-image: url('$(res)/img/element-icons/message/fwd.svg');
59+
}
60+
61+
.mx_MessageContextMenu_iconRedact::before {
62+
mask-image: url('$(res)/img/element-icons/trashcan.svg');
63+
}
64+
65+
.mx_MessageContextMenu_iconResend::before {
66+
mask-image: url('$(res)/img/element-icons/retry.svg');
67+
}
68+
69+
.mx_MessageContextMenu_iconSource::before {
70+
mask-image: url('$(res)/img/element-icons/room/format-bar/code.svg');
71+
}
72+
73+
.mx_MessageContextMenu_iconQuote::before {
74+
mask-image: url('$(res)/img/element-icons/room/format-bar/quote.svg');
75+
}
76+
77+
.mx_MessageContextMenu_iconPin::before {
78+
mask-image: url('$(res)/img/element-icons/room/pin-upright.svg');
79+
}
2780

28-
.mx_MessageContextMenu_field.mx_MessageContextMenu_fieldSet {
29-
font-weight: bold;
81+
.mx_MessageContextMenu_iconUnpin::before {
82+
mask-image: url('$(res)/img/element-icons/room/pin.svg');
83+
}
3084
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

res/img/element-icons/message/fwd.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 31 additions & 4 deletions
Loading

src/components/views/context_menus/MessageContextMenu.js

Lines changed: 85 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import Resend from '../../../Resend';
2828
import SettingsStore from '../../../settings/SettingsStore';
2929
import { isUrlPermitted } from '../../../HtmlUtils';
3030
import { isContentActionable } from '../../../utils/EventUtils';
31-
import { MenuItem } from "../../structures/ContextMenu";
31+
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu';
3232
import { EventType } from "matrix-js-sdk/src/@types/event";
3333
import { replaceableComponent } from "../../../utils/replaceableComponent";
3434
import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard";
@@ -257,55 +257,68 @@ export default class MessageContextMenu extends React.Component {
257257
let externalURLButton;
258258
let quoteButton;
259259
let collapseReplyThread;
260+
let redactItemList;
260261

261262
// status is SENT before remote-echo, null after
262263
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
263264
if (!mxEvent.isRedacted()) {
264265
if (unsentReactionsCount !== 0) {
265266
resendReactionsButton = (
266-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onResendReactionsClick}>
267-
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
268-
</MenuItem>
267+
<IconizedContextMenuOption
268+
iconClassName="mx_MessageContextMenu_iconResend"
269+
label={ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
270+
onClick={this.onResendReactionsClick}
271+
/>
269272
);
270273
}
271274
}
272275

273276
if (isSent && this.state.canRedact) {
274277
redactButton = (
275-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
276-
{ _t('Remove') }
277-
</MenuItem>
278+
<IconizedContextMenuOption
279+
iconClassName="mx_MessageContextMenu_iconRedact"
280+
label={_t("Remove")}
281+
onClick={this.onRedactClick}
282+
/>
278283
);
279284
}
280285

281286
if (isContentActionable(mxEvent)) {
282287
forwardButton = (
283-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onForwardClick}>
284-
{ _t('Forward Message') }
285-
</MenuItem>
288+
<IconizedContextMenuOption
289+
iconClassName="mx_MessageContextMenu_iconForward"
290+
label={_t("Forward")}
291+
onClick={this.onForwardClick}
292+
/>
286293
);
287294

288295
if (this.state.canPin) {
289296
pinButton = (
290-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onPinClick}>
291-
{ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
292-
</MenuItem>
297+
<IconizedContextMenuOption
298+
iconClassName="mx_MessageContextMenu_iconPin"
299+
label={ this._isPinned() ? _t('Unpin') : _t('Pin') }
300+
onClick={this.onPinClick}
301+
/>
293302
);
294303
}
295304
}
296305

297306
const viewSourceButton = (
298-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}>
299-
{ _t('View Source') }
300-
</MenuItem>
307+
<IconizedContextMenuOption
308+
iconClassName="mx_MessageContextMenu_iconSource"
309+
label={_t("View source")}
310+
onClick={this.onViewSourceClick}
311+
/>
301312
);
302313

303314
if (this.props.eventTileOps) {
304315
if (this.props.eventTileOps.isWidgetHidden()) {
305316
unhidePreviewButton = (
306-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}>
307-
{ _t('Unhide Preview') }
308-
</MenuItem>
317+
<IconizedContextMenuOption
318+
iconClassName="mx_MessageContextMenu_iconUnhidePreview"
319+
label={_t("Show preview")}
320+
onClick={this.onUnhidePreviewClick}
321+
/>
309322
);
310323
}
311324
}
@@ -316,77 +329,97 @@ export default class MessageContextMenu extends React.Component {
316329
}
317330
// 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)
318331
const permalinkButton = (
319-
<MenuItem
320-
element="a"
321-
className="mx_MessageContextMenu_field"
332+
<IconizedContextMenuOption
333+
iconClassName="mx_MessageContextMenu_iconPermalink"
322334
onClick={this.onPermalinkClick}
335+
label= {_t('Share')}
336+
element="a"
323337
href={permalink}
324338
target="_blank"
325339
rel="noreferrer noopener"
326-
>
327-
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
328-
? _t('Share Permalink') : _t('Share Message') }
329-
</MenuItem>
340+
/>
330341
);
331342

332343
if (this.props.eventTileOps) { // this event is rendered using TextualBody
333344
quoteButton = (
334-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onQuoteClick}>
335-
{ _t('Quote') }
336-
</MenuItem>
345+
<IconizedContextMenuOption
346+
iconClassName="mx_MessageContextMenu_iconQuote"
347+
label={_t("Quote")}
348+
onClick={this.onQuoteClick}
349+
/>
337350
);
338351
}
339352

340353
// Bridges can provide a 'external_url' to link back to the source.
341-
if (
342-
typeof(mxEvent.event.content.external_url) === "string" &&
354+
if (typeof (mxEvent.event.content.external_url) === "string" &&
343355
isUrlPermitted(mxEvent.event.content.external_url)
344356
) {
345357
externalURLButton = (
346-
<MenuItem
358+
<IconizedContextMenuOption
359+
iconClassName="mx_MessageContextMenu_iconLink"
360+
onClick={this.closeMenu}
361+
label={ _t('Source URL') }
347362
element="a"
348-
className="mx_MessageContextMenu_field"
349363
target="_blank"
350364
rel="noreferrer noopener"
351-
onClick={this.closeMenu}
352365
href={mxEvent.event.content.external_url}
353-
>
354-
{ _t('Source URL') }
355-
</MenuItem>
366+
/>
356367
);
357368
}
358369

359370
if (this.props.collapseReplyThread) {
360371
collapseReplyThread = (
361-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onCollapseReplyThreadClick}>
362-
{ _t('Collapse Reply Thread') }
363-
</MenuItem>
372+
<IconizedContextMenuOption
373+
iconClassName="mx_MessageContextMenu_iconCollapse"
374+
label={_t("Collapse reply thread")}
375+
onClick={this.onCollapseReplyThreadClick}
376+
/>
364377
);
365378
}
366379

367380
let reportEventButton;
368381
if (mxEvent.getSender() !== me) {
369382
reportEventButton = (
370-
<MenuItem className="mx_MessageContextMenu_field" onClick={this.onReportEventClick}>
371-
{ _t('Report Content') }
372-
</MenuItem>
383+
<IconizedContextMenuOption
384+
iconClassName="mx_MessageContextMenu_iconReport"
385+
label={_t("Report")}
386+
onClick={this.onReportEventClick}
387+
/>
373388
);
374389
}
375390

376-
return (
377-
<div className="mx_MessageContextMenu">
378-
{ resendReactionsButton }
379-
{ redactButton }
391+
const commonItemsList = (
392+
<IconizedContextMenuOptionList>
393+
{ quoteButton }
380394
{ forwardButton }
381395
{ pinButton }
382-
{ viewSourceButton }
383-
{ unhidePreviewButton }
384396
{ permalinkButton }
385-
{ quoteButton }
397+
{ reportEventButton }
386398
{ externalURLButton }
399+
{ unhidePreviewButton }
400+
{ viewSourceButton }
401+
{ resendReactionsButton }
387402
{ collapseReplyThread }
388-
{ reportEventButton }
389-
</div>
403+
</IconizedContextMenuOptionList>
404+
);
405+
406+
if (redactButton) {
407+
redactItemList = (
408+
<IconizedContextMenuOptionList red>
409+
{ redactButton }
410+
</IconizedContextMenuOptionList>
411+
);
412+
}
413+
414+
return (
415+
<IconizedContextMenu
416+
{...this.props}
417+
className="mx_MessageContextMenu"
418+
compact={true}
419+
>
420+
{ commonItemsList }
421+
{ redactItemList }
422+
</IconizedContextMenu>
390423
);
391424
}
392425
}

src/components/views/messages/MessageActionBar.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,14 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo
4848
const replyThread = getReplyThread && getReplyThread();
4949

5050
const buttonRect = button.current.getBoundingClientRect();
51-
contextMenu = <ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu}>
52-
<MessageContextMenu
53-
mxEvent={mxEvent}
54-
permalinkCreator={permalinkCreator}
55-
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
56-
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
57-
onFinished={closeMenu}
58-
/>
59-
</ContextMenu>;
51+
contextMenu = <MessageContextMenu
52+
{...aboveLeftOf(buttonRect)}
53+
mxEvent={mxEvent}
54+
permalinkCreator={permalinkCreator}
55+
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
56+
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
57+
onFinished={closeMenu}
58+
/>;
6059
}
6160

6261
return <React.Fragment>

0 commit comments

Comments
 (0)