Merge branch 'unstable' into userconfig_disappearingmessage

pull/2971/head
William Grant 8 months ago
commit 2a05185138

@ -2,7 +2,7 @@
"name": "session-desktop",
"productName": "Session",
"description": "Private messaging from your desktop",
"version": "1.11.1",
"version": "1.11.2",
"license": "GPL-3.0",
"author": {
"name": "Oxen Labs",
@ -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",

@ -6,7 +6,7 @@ body.rtl {
.right-panel-item,
.contact-selection-list,
.group-member-list__selection,
.react-contexify__item {
.contexify_item {
direction: rtl;
}

@ -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(.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';
@ -66,7 +66,7 @@ type Props = { messageId: string; contextMenuId: string; enableReactions: boolea
const StyledMessageContextMenu = styled.div`
position: relative;
.react-contexify {
.contexify {
margin-left: -104px;
}
`;
@ -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,30 @@ 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.
// Note: If you change this, also make sure to update the `handleContextMenu()` in GenericReadableMessage.tsx
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 +349,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>

@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useInterval } from 'react-use';
import { useInterval, useMount } from 'react-use';
import styled from 'styled-components';
import { Data } from '../../../../data/data';
import { getConversationController } from '../../../../session/conversations';
@ -61,9 +61,9 @@ function useIsExpired(
checkFrequency = Math.max(EXPIRATION_CHECK_MINIMUM, increment);
}
useEffect(() => {
useMount(() => {
void checkExpired();
}, [checkExpired]); // check on mount
}); // check on mount
useInterval(checkExpired, checkFrequency); // check every 2sec or sooner if needed

@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import { contextMenu } from 'react-contexify';
import { useSelector } from 'react-redux';
import styled, { keyframes } from 'styled-components';
import { isNil, isString, toNumber } from 'lodash';
import { MessageRenderingProps } from '../../../../models/messageType';
import { getConversationController } from '../../../../session/conversations';
import {
@ -99,13 +100,27 @@ export const GenericReadableMessage = (props: Props) => {
const handleContextMenu = useCallback(
(e: React.MouseEvent<HTMLElement>) => {
// 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.
// Note: If you change this, also make sure to update the `saveAttachment()` in MessageContextMenu.tsx
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, Menu } from 'react-contexify';
import { Menu } from 'react-contexify';
import { useSelector } from 'react-redux';
import { isSearching } from '../../state/selectors/search';
import {
@ -58,7 +58,7 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
return (
<ContextConversationProvider value={convoId}>
<SessionContextMenuContainer>
<Menu id={triggerId} animation={animation.fade}>
<Menu id={triggerId} animation="fade">
<NotificationForConvoMenuItem />
<BlockMenuItem />
<CopyMenuItem />

@ -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>

@ -546,7 +546,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// an OpenGroupV2 message is just a visible message
const chatMessageParams: VisibleMessageParams = {
body: '',
timestamp: sentAt,
// we need to use a new timestamp here, otherwise android&iOS will consider this message as a duplicate and drop the synced reaction
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
reaction,
lokiProfile: UserUtils.getOurProfile(),
};

@ -35,6 +35,7 @@ import { getAllCachedECKeyPair, sentAtMoreRecentThanWrapper } from './closedGrou
import { ConfigMessageHandler } from './configMessage';
import { ECKeyPair } from './keypairs';
import { ContactsWrapperActions } from '../webworker/workers/browser/libsession_worker_interface';
import { isUsFromCache } from '../session/utils/User';
import {
changeToDisappearingMessageType,
checkForExpireUpdateInContentMessage,
@ -298,27 +299,42 @@ async function decrypt(envelope: EnvelopePlus): Promise<any> {
return plaintext;
}
async function shouldDropIncomingPrivateMessage(sentAtTimestamp: number, envelope: EnvelopePlus) {
async function shouldDropIncomingPrivateMessage(
sentAtTimestamp: number,
envelope: EnvelopePlus,
content: SignalService.Content
) {
// sentAtMoreRecentThanWrapper is going to be true, if the latest contact wrapper we processed was roughly more recent that this message timestamp
const moreRecentOrNah = await sentAtMoreRecentThanWrapper(sentAtTimestamp, 'ContactsConfig');
const isSyncedMessage = isUsFromCache(envelope.source);
if (moreRecentOrNah === 'wrapper_more_recent') {
// we need to check if that conversation is already in the wrapper, and if yes
// we need to check if that conversation is already in the wrapper
try {
const privateConvoInWrapper = await ContactsWrapperActions.get(envelope.source);
// let's check if the corresponding conversation is hidden in the contacts wrapper or not.
// the corresponding conversation is syncTarget when this is a synced message only, so we need to rely on it first, then the envelope.source.
const syncTargetOrSource = isSyncedMessage
? content.dataMessage?.syncTarget || undefined
: envelope.source;
if (!syncTargetOrSource) {
return false;
}
const privateConvoInWrapper = await ContactsWrapperActions.get(syncTargetOrSource);
if (
!privateConvoInWrapper ||
privateConvoInWrapper.priority <= CONVERSATION_PRIORITIES.hidden
) {
// the wrapper is more recent that this message and there is no such private conversation. Just drop this incoming message.
window.log.info(
`received message from contact ${envelope.source} which appears to be hidden/removed in our most recent libsession contactconfig, sentAt: ${sentAtTimestamp}. Dropping it`
`received message on conversation ${syncTargetOrSource} which appears to be hidden/removed in our most recent libsession contactconfig, sentAt: ${sentAtTimestamp}. Dropping it`
);
return true;
}
window.log.info(
`received message from contact ${envelope.source} which appears to NOT be hidden/removed in our most recent libsession contactconfig, sentAt: ${sentAtTimestamp}. `
`received message on conversation ${syncTargetOrSource} which appears to NOT be hidden/removed in our most recent libsession contactconfig, sentAt: ${sentAtTimestamp}. `
);
} catch (e) {
window.log.warn(
@ -415,7 +431,7 @@ export async function innerHandleSwarmContentMessage(
const isPrivateConversationMessage = !envelope.senderIdentity;
if (isPrivateConversationMessage) {
if (await shouldDropIncomingPrivateMessage(sentAtTimestamp, envelope)) {
if (await shouldDropIncomingPrivateMessage(sentAtTimestamp, envelope, content)) {
await removeFromCache(envelope);
return;
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save