fix: fix save as menu item + bump react-contexify to 6.0.0
This commit is contained in:
parent
eb4631ff99
commit
9199c7b529
|
@ -109,7 +109,7 @@
|
|||
"protobufjs": "^7.2.4",
|
||||
"rc-slider": "^10.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-contexify": "5.0.0",
|
||||
"react-contexify": "^6.0.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-draggable": "^4.4.4",
|
||||
"react-h5-audio-player": "^3.2.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Modules
|
||||
@import 'react-h5-audio-player/lib/styles.css';
|
||||
@import 'react-contexify/dist/ReactContexify.min.css';
|
||||
@import 'react-contexify/dist/ReactContexify.css'; // the .css is actually referencing the .min.css in the 6.0.0
|
||||
@import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
@import 'sanitize.css/sanitize.css';
|
||||
|
|
|
@ -3,36 +3,35 @@ import styled from 'styled-components';
|
|||
export const SessionContextMenuContainer = styled.div.attrs({
|
||||
// custom props
|
||||
})`
|
||||
.react-contexify {
|
||||
.contexify {
|
||||
// be sure it is more than the one set for the More Information screen of messages
|
||||
z-index: 30;
|
||||
min-width: 200px;
|
||||
box-shadow: 0px 0px 10px var(--context-menu-shadow-color) !important;
|
||||
background-color: var(--context-menu-background-color);
|
||||
|
||||
&.react-contexify__theme--dark {
|
||||
&.contexify_theme-dark {
|
||||
background-color: var(--context-menu-background-color);
|
||||
}
|
||||
|
||||
.react-contexify__item {
|
||||
.contexify_item {
|
||||
background: var(--context-menu-background-color);
|
||||
}
|
||||
|
||||
.react-contexify__item:not(.react-contexify__item--disabled):hover
|
||||
> .react-contexify__item__content {
|
||||
.contexify_item:not(.react-contexify_item-disabled):hover > .contexify_itemContent {
|
||||
background: var(--context-menu-background-hover-color);
|
||||
color: var(--context-menu-text-hover-color);
|
||||
}
|
||||
.react-contexify__item__content {
|
||||
.contexify_itemContent {
|
||||
transition: var(--default-duration);
|
||||
color: var(--context-menu-text-color);
|
||||
}
|
||||
|
||||
&.react-contexify__submenu {
|
||||
&.contexify_submenu {
|
||||
top: -28px !important; // height of an item element
|
||||
}
|
||||
|
||||
.react-contexify__submenu-arrow {
|
||||
.contexify_submenu-arrow {
|
||||
line-height: 16px; // center the arrow for submenu
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { animation, contextMenu, Item, Menu } from 'react-contexify';
|
||||
import { contextMenu, Item, Menu } from 'react-contexify';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
|
||||
|
@ -20,7 +20,7 @@ const VideoInputMenu = ({
|
|||
}) => {
|
||||
return (
|
||||
<SessionContextMenuContainer>
|
||||
<Menu id={triggerId} animation={animation.fade}>
|
||||
<Menu id={triggerId} animation="fade">
|
||||
{camerasList.map(m => {
|
||||
return (
|
||||
<Item
|
||||
|
@ -93,7 +93,7 @@ const AudioInputMenu = ({
|
|||
}) => {
|
||||
return (
|
||||
<SessionContextMenuContainer>
|
||||
<Menu id={triggerId} animation={animation.fade}>
|
||||
<Menu id={triggerId} animation="fade">
|
||||
{audioInputsList.map(m => {
|
||||
return (
|
||||
<Item
|
||||
|
@ -162,7 +162,7 @@ const AudioOutputMenu = ({
|
|||
}) => {
|
||||
return (
|
||||
<SessionContextMenuContainer>
|
||||
<Menu id={triggerId} animation={animation.fade}>
|
||||
<Menu id={triggerId} animation="fade">
|
||||
{audioOutputsList.map(m => {
|
||||
return (
|
||||
<Item
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { animation, Item, Menu, useContextMenu } from 'react-contexify';
|
||||
|
||||
import { Item, ItemParams, Menu, useContextMenu } from 'react-contexify';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useClickAway, useMouse } from 'react-use';
|
||||
import styled from 'styled-components';
|
||||
import { isNumber } from 'lodash';
|
||||
import { Data } from '../../../../data/data';
|
||||
|
||||
import { MessageInteraction } from '../../../../interactions';
|
||||
|
@ -103,49 +103,6 @@ const DeleteForEveryone = ({ messageId }: { messageId: string }) => {
|
|||
|
||||
type MessageId = { messageId: string };
|
||||
|
||||
const SaveAttachment = ({ messageId }: MessageId) => {
|
||||
const convoId = useSelectedConversationKey();
|
||||
const attachments = useMessageAttachments(messageId);
|
||||
const timestamp = useMessageTimestamp(messageId);
|
||||
const serverTimestamp = useMessageServerTimestamp(messageId);
|
||||
|
||||
const sender = useMessageSender(messageId);
|
||||
const saveAttachment = useCallback(
|
||||
(e: any) => {
|
||||
// this is quite dirty but considering that we want the context menu of the message to show on click on the attachment
|
||||
// and the context menu save attachment item to save the right attachment I did not find a better way for now.
|
||||
let targetAttachmentIndex = e.triggerEvent.path[1].getAttribute('data-attachmentindex');
|
||||
e.event.stopPropagation();
|
||||
if (!attachments?.length || !convoId || !sender) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetAttachmentIndex) {
|
||||
targetAttachmentIndex = 0;
|
||||
}
|
||||
if (targetAttachmentIndex > attachments.length) {
|
||||
return;
|
||||
}
|
||||
const messageTimestamp = timestamp || serverTimestamp || 0;
|
||||
void saveAttachmentToDisk({
|
||||
attachment: attachments[targetAttachmentIndex],
|
||||
messageTimestamp,
|
||||
messageSender: sender,
|
||||
conversationId: convoId,
|
||||
});
|
||||
},
|
||||
[convoId, sender, attachments, serverTimestamp, timestamp]
|
||||
);
|
||||
|
||||
if (!convoId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return attachments?.length ? (
|
||||
<Item onClick={saveAttachment}>{window.i18n('downloadAttachment')}</Item>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const AdminActionItems = ({ messageId }: MessageId) => {
|
||||
const convoId = useSelectedConversationKey();
|
||||
const isPublic = useSelectedIsPublic();
|
||||
|
@ -218,6 +175,11 @@ export const MessageContextMenu = (props: Props) => {
|
|||
const status = useMessageStatus(messageId);
|
||||
const isDeletable = useMessageIsDeletable(messageId);
|
||||
const text = useMessageBody(messageId);
|
||||
const attachments = useMessageAttachments(messageId);
|
||||
const timestamp = useMessageTimestamp(messageId);
|
||||
const serverTimestamp = useMessageServerTimestamp(messageId);
|
||||
|
||||
const sender = useMessageSender(messageId);
|
||||
|
||||
const isOutgoing = direction === 'outgoing';
|
||||
const isSent = status === 'sent' || status === 'read'; // a read message should be replyable
|
||||
|
@ -233,21 +195,24 @@ export const MessageContextMenu = (props: Props) => {
|
|||
const [mouseX, setMouseX] = useState(0);
|
||||
const [mouseY, setMouseY] = useState(0);
|
||||
|
||||
const onContextMenuShown = () => {
|
||||
if (showEmojiPanel) {
|
||||
setShowEmojiPanel(false);
|
||||
}
|
||||
window.contextMenuShown = true;
|
||||
};
|
||||
|
||||
const onContextMenuHidden = useCallback(() => {
|
||||
// This function will called before the click event
|
||||
// on the message would trigger (and I was unable to
|
||||
// prevent propagation in this case), so use a short timeout
|
||||
setTimeout(() => {
|
||||
window.contextMenuShown = false;
|
||||
}, 100);
|
||||
}, []);
|
||||
const onVisibilityChange = useCallback(
|
||||
(isVisible: boolean) => {
|
||||
if (isVisible) {
|
||||
if (showEmojiPanel) {
|
||||
setShowEmojiPanel(false);
|
||||
}
|
||||
window.contextMenuShown = true;
|
||||
return;
|
||||
}
|
||||
// This function will called before the click event
|
||||
// on the message would trigger (and I was unable to
|
||||
// prevent propagation in this case), so use a short timeout
|
||||
setTimeout(() => {
|
||||
window.contextMenuShown = false;
|
||||
}, 100);
|
||||
},
|
||||
[showEmojiPanel]
|
||||
);
|
||||
|
||||
const onShowDetail = async () => {
|
||||
const found = await Data.getMessageById(messageId);
|
||||
|
@ -313,6 +278,29 @@ export const MessageContextMenu = (props: Props) => {
|
|||
}
|
||||
};
|
||||
|
||||
const saveAttachment = (e: ItemParams) => {
|
||||
// this is quite dirty but considering that we want the context menu of the message to show on click on the attachment
|
||||
// and the context menu save attachment item to save the right attachment I did not find a better way for now.
|
||||
const targetAttachmentIndex = isNumber(e?.props?.dataAttachmentIndex)
|
||||
? e.props.dataAttachmentIndex
|
||||
: 0;
|
||||
e.event.stopPropagation();
|
||||
if (!attachments?.length || !convoId || !sender) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetAttachmentIndex > attachments.length) {
|
||||
return;
|
||||
}
|
||||
const messageTimestamp = timestamp || serverTimestamp || 0;
|
||||
void saveAttachmentToDisk({
|
||||
attachment: attachments[targetAttachmentIndex],
|
||||
messageTimestamp,
|
||||
messageSender: sender,
|
||||
conversationId: convoId,
|
||||
});
|
||||
};
|
||||
|
||||
useClickAway(emojiPanelRef, () => {
|
||||
onEmojiLoseFocus();
|
||||
});
|
||||
|
@ -360,18 +348,14 @@ export const MessageContextMenu = (props: Props) => {
|
|||
</StyledEmojiPanelContainer>
|
||||
)}
|
||||
<SessionContextMenuContainer>
|
||||
<Menu
|
||||
id={contextMenuId}
|
||||
onShown={onContextMenuShown}
|
||||
onHidden={onContextMenuHidden}
|
||||
animation={animation.fade}
|
||||
>
|
||||
<Menu id={contextMenuId} onVisibilityChange={onVisibilityChange} animation="fade">
|
||||
{enableReactions && (
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
<MessageReactBar action={onEmojiClick} additionalAction={onShowEmoji} />
|
||||
)}
|
||||
<SaveAttachment messageId={messageId} />
|
||||
|
||||
{attachments?.length ? (
|
||||
<Item onClick={saveAttachment}>{window.i18n('downloadAttachment')}</Item>
|
||||
) : null}
|
||||
<Item onClick={copyText}>{window.i18n('copyMessage')}</Item>
|
||||
{(isSent || !isOutgoing) && (
|
||||
<Item onClick={onReply}>{window.i18n('replyToMessage')}</Item>
|
||||
|
|
|
@ -7,6 +7,7 @@ import styled, { keyframes } from 'styled-components';
|
|||
import useInterval from 'react-use/lib/useInterval';
|
||||
import useMount from 'react-use/lib/useMount';
|
||||
|
||||
import { isNil, isString, toNumber } from 'lodash';
|
||||
import { Data } from '../../../../data/data';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
|
@ -180,12 +181,22 @@ export const GenericReadableMessage = (props: Props) => {
|
|||
const handleContextMenu = useCallback(
|
||||
(e: React.MouseEvent<HTMLElement>) => {
|
||||
const enableContextMenu = !multiSelectMode && !msgProps?.isKickedFromGroup;
|
||||
const attachmentIndexStr = (e?.target as any)?.parentElement?.getAttribute?.(
|
||||
'data-attachmentindex'
|
||||
);
|
||||
const attachmentIndex =
|
||||
isString(attachmentIndexStr) && !isNil(toNumber(attachmentIndexStr))
|
||||
? toNumber(attachmentIndexStr)
|
||||
: 0;
|
||||
|
||||
if (enableContextMenu) {
|
||||
contextMenu.hideAll();
|
||||
contextMenu.show({
|
||||
id: ctxMenuID,
|
||||
event: e,
|
||||
props: {
|
||||
dataAttachmentIndex: attachmentIndex,
|
||||
},
|
||||
});
|
||||
}
|
||||
setIsRightClicked(enableContextMenu);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { animation, Item, Menu, Submenu } from 'react-contexify';
|
||||
import { Item, Menu, Submenu } from 'react-contexify';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { setDisappearingMessagesByConvoId } from '../../interactions/conversationInteractions';
|
||||
import { isSearching } from '../../state/selectors/search';
|
||||
|
@ -64,7 +64,7 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
|
|||
return (
|
||||
<ContextConversationProvider value={convoId}>
|
||||
<SessionContextMenuContainer>
|
||||
<Menu id={triggerId} animation={animation.fade}>
|
||||
<Menu id={triggerId} animation="fade">
|
||||
<DisappearingMessageMenuItem />
|
||||
<NotificationForConvoMenuItem />
|
||||
<BlockMenuItem />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { animation, Item, Menu } from 'react-contexify';
|
||||
import { Item, Menu } from 'react-contexify';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useIsPinned, useIsPrivate, useIsPrivateAndFriend } from '../../hooks/useParamSelector';
|
||||
|
@ -44,7 +44,7 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) =>
|
|||
|
||||
return (
|
||||
<SessionContextMenuContainer>
|
||||
<Menu id={triggerId} animation={animation.fade}>
|
||||
<Menu id={triggerId} animation="fade">
|
||||
{/* Message request related actions */}
|
||||
<AcceptMsgRequestMenuItem />
|
||||
<DeclineMsgRequestMenuItem />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { animation, Item, Menu } from 'react-contexify';
|
||||
import { Item, Menu } from 'react-contexify';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { SessionContextMenuContainer } from '../SessionContextMenuContainer';
|
||||
|
@ -28,7 +28,7 @@ export const MessageRequestBannerContextMenu = (props: PropsContextConversationI
|
|||
|
||||
return (
|
||||
<SessionContextMenuContainer>
|
||||
<Menu id={triggerId} animation={animation.fade}>
|
||||
<Menu id={triggerId} animation="fade">
|
||||
<HideBannerMenuItem />
|
||||
</Menu>
|
||||
</SessionContextMenuContainer>
|
||||
|
|
Loading…
Reference in New Issue