fix issue with contextmenu on scroll causing UI to break

pull/1387/head
Audric Ackermann 5 years ago
parent a7c4ce77a1
commit 8abd6a0e21
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -756,7 +756,7 @@ label {
.react-contexify { .react-contexify {
z-index: 3; z-index: 3;
min-width: 200px; min-width: 200px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3), 0 0 0 1px #eee; box-shadow: none;
.react-contexify__item:not(.react-contexify__item--disabled):hover .react-contexify__item:not(.react-contexify__item--disabled):hover
> .react-contexify__item__content { > .react-contexify__item__content {

@ -35,7 +35,8 @@ import { getIncrement } from '../../util/timer';
import { isFileDangerous } from '../../util/isFileDangerous'; import { isFileDangerous } from '../../util/isFileDangerous';
import { SessionIcon, SessionIconSize, SessionIconType } from '../session/icon'; import { SessionIcon, SessionIconSize, SessionIconType } from '../session/icon';
import _ from 'lodash'; import _ from 'lodash';
import { animation, Item, Menu, MenuProvider, theme } from 'react-contexify'; import { animation, contextMenu, Item, Menu } from 'react-contexify';
import uuid from 'uuid';
declare global { declare global {
interface Window { interface Window {
@ -130,25 +131,25 @@ const EXPIRATION_CHECK_MINIMUM = 2000;
const EXPIRED_DELAY = 600; const EXPIRED_DELAY = 600;
export class Message extends React.PureComponent<Props, State> { export class Message extends React.PureComponent<Props, State> {
public captureMenuTriggerBound: (trigger: any) => void;
public handleImageErrorBound: () => void; public handleImageErrorBound: () => void;
public menuTriggerRef: Trigger | undefined;
public expirationCheckInterval: any; public expirationCheckInterval: any;
public expiredTimeout: any; public expiredTimeout: any;
public ctxMenuID: string;
public constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.captureMenuTriggerBound = this.captureMenuTrigger.bind(this);
this.handleImageErrorBound = this.handleImageError.bind(this); this.handleImageErrorBound = this.handleImageError.bind(this);
this.onReplyPrivate = this.onReplyPrivate.bind(this); this.onReplyPrivate = this.onReplyPrivate.bind(this);
this.handleContextMenu = this.handleContextMenu.bind(this);
this.state = { this.state = {
expiring: false, expiring: false,
expired: false, expired: false,
imageBroken: false, imageBroken: false,
}; };
this.ctxMenuID = uuid();
} }
public componentDidMount() { public componentDidMount() {
@ -180,6 +181,7 @@ export class Message extends React.PureComponent<Props, State> {
this.checkExpired(); this.checkExpired();
} }
public checkExpired() { public checkExpired() {
const now = Date.now(); const now = Date.now();
const { isExpired, expirationTimestamp, expirationLength } = this.props; const { isExpired, expirationTimestamp, expirationLength } = this.props;
@ -783,16 +785,7 @@ export class Message extends React.PureComponent<Props, State> {
); );
} }
public captureMenuTrigger(triggerRef: Trigger) { public renderContextMenu() {
this.menuTriggerRef = triggerRef;
}
public showMenu(event: React.MouseEvent<HTMLDivElement>) {
if (this.menuTriggerRef) {
this.menuTriggerRef.handleContextClick(event);
}
}
public renderContextMenu(triggerId: string) {
const { const {
attachments, attachments,
onCopyText, onCopyText,
@ -843,7 +836,7 @@ export class Message extends React.PureComponent<Props, State> {
return ( return (
<Menu <Menu
id={triggerId} id={this.ctxMenuID}
onShown={onContextMenuShown} onShown={onContextMenuShown}
onHidden={onContextMenuHidden} onHidden={onContextMenuHidden}
animation={animation.fade} animation={animation.fade}
@ -963,10 +956,7 @@ export class Message extends React.PureComponent<Props, State> {
} = this.props; } = this.props;
const { expired, expiring } = this.state; const { expired, expiring } = this.state;
// The Date.now() is a workaround to be sure a single triggerID with this id exists
const rightClickTriggerId = id
? String(`message-ctx-${id}-${Date.now()}`)
: String(`message-ctx-${authorPhoneNumber}-${timestamp}`);
if (expired) { if (expired) {
return null; return null;
} }
@ -997,11 +987,8 @@ export class Message extends React.PureComponent<Props, State> {
divClasses.push('public-chat-message-wrapper'); divClasses.push('public-chat-message-wrapper');
} }
const enableContextMenu = !isRss && !multiSelectMode && !isKickedFromGroup;
return ( return (
<div id={id} className={classNames(divClasses)}> <div id={id} className={classNames(divClasses)} onContextMenu={this.handleContextMenu}>
<MenuProvider id={rightClickTriggerId}>
{this.renderAvatar()} {this.renderAvatar()}
<div <div
className={classNames( className={classNames(
@ -1019,7 +1006,7 @@ export class Message extends React.PureComponent<Props, State> {
// User clicked on message body // User clicked on message body
const target = event.target as HTMLDivElement; const target = event.target as HTMLDivElement;
if (!multiSelectMode && target.className === 'text-selectable') { if (!multiSelectMode && target.className === 'text-selectable' || window.contextMenuShown) {
return; return;
} }
@ -1048,7 +1035,7 @@ export class Message extends React.PureComponent<Props, State> {
// User clicked on message body // User clicked on message body
const target = event.target as HTMLDivElement; const target = event.target as HTMLDivElement;
if (target.className === 'text-selectable') { if (target.className === 'text-selectable' || window.contextMenuShown) {
return; return;
} }
@ -1066,15 +1053,32 @@ export class Message extends React.PureComponent<Props, State> {
{this.renderMetadata()} {this.renderMetadata()}
</div> </div>
{this.renderError(!isIncoming)} {this.renderError(!isIncoming)}
{enableContextMenu {this.renderContextMenu()}
? this.renderContextMenu(rightClickTriggerId)
: null}
</div> </div>
</MenuProvider>
</div> </div>
); );
} }
private handleContextMenu(e: any) {
e.preventDefault();
e.stopPropagation();
const {
isRss,
multiSelectMode,
isKickedFromGroup,
} = this.props;
const enableContextMenu = !isRss && !multiSelectMode && !isKickedFromGroup;
if (enableContextMenu) {
// Don't forget to pass the id and the event and voila!
contextMenu.hideAll();
contextMenu.show({
id: this.ctxMenuID,
event: e,
});
}
}
private renderAuthor() { private renderAuthor() {
const { const {
authorName, authorName,
@ -1114,6 +1118,7 @@ export class Message extends React.PureComponent<Props, State> {
private onReplyPrivate(e: any) { private onReplyPrivate(e: any) {
e.event.stopPropagation(); e.event.stopPropagation();
e.event.preventDefault();
if (this.props && this.props.onReply) { if (this.props && this.props.onReply) {
this.props.onReply(this.props.timestamp); this.props.onReply(this.props.timestamp);
} }

@ -292,6 +292,7 @@ export class SessionConversationMessagesList extends React.Component<
if (!messageContainer) { if (!messageContainer) {
return; return;
} }
contextMenu.hideAll();
const scrollTop = messageContainer.scrollTop; const scrollTop = messageContainer.scrollTop;
const scrollHeight = messageContainer.scrollHeight; const scrollHeight = messageContainer.scrollHeight;

Loading…
Cancel
Save