From 19e9e0311e0791970e7e3874228191023a835936 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 6 Dec 2023 11:46:49 +1100 Subject: [PATCH] feat: add cleanup of old expire update in history we only keep one from each sender --- _locales/en/messages.json | 20 +++--- package.json | 2 +- ts/components/basic/Text.tsx | 8 ++- .../conversation/TimerNotification.tsx | 7 +- .../message/message-content/MessageStatus.tsx | 30 +++++++-- ts/data/data.ts | 14 ++++ ts/data/dataInit.ts | 1 + ts/models/conversation.ts | 51 +++++++++------ ts/node/migration/sessionMigrations.ts | 2 + ts/node/sql.ts | 64 ++++++++++++++++--- ts/receiver/closedGroups.ts | 1 - ts/receiver/configMessage.ts | 1 + ts/receiver/contentMessage.ts | 1 - ts/session/disappearing_messages/index.ts | 4 +- ts/session/disappearing_messages/legacy.ts | 15 ++--- ts/session/sending/MessageSender.ts | 64 ++++++++++--------- .../libsession_utils_user_profile.ts | 1 - .../DisappearingMessage_test.ts | 1 - yarn.lock | 30 ++++----- 19 files changed, 209 insertions(+), 108 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1de0dcdf5..26ef1abdb 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -192,10 +192,10 @@ "followSetting": "Follow Setting", "followSettingDisabled": "Messages you send will no longer disappear. Are you sure you want to turn off disappearing messages?", "followSettingTimeAndType": "Set your messages to disappear $time$ after they have been $type$?", - "youChangedTheTimer": "You have set messages to disappear $time$ after they have been $mode$", - "youChangedTheTimerLegacy": "You set the disappearing message timer to $time$", - "theyChangedTheTimer": "$name$ has set messages to disappear $time$ after they have been $mode$", - "theyChangedTheTimerLegacy": "$name$ set the disappearing message timer to $time$", + "youChangedTheTimer": "You have set messages to disappear $time$ after they have been $mode$", + "youChangedTheTimerLegacy": "You set the disappearing message timer to $time$", + "theyChangedTheTimer": "$name$ has set messages to disappear $time$ after they have been $mode$", + "theyChangedTheTimerLegacy": "$name$ set the disappearing message timer to $time$", "timerOption_0_seconds": "Off", "timerOption_5_seconds": "5 seconds", "timerOption_10_seconds": "10 seconds", @@ -233,12 +233,12 @@ "disappearingMessagesModeLegacy": "Legacy", "disappearingMessagesModeLegacySubtitle": "Original version of disappearing messages.", "disappearingMessagesDisabled": "Disappearing messages disabled", - "disabledDisappearingMessages": "$name$ has turned off disappearing messages.", - "youDisabledDisappearingMessages": "You have turned off disappearing messages.", - "youDisabledYourDisappearingMessages": "You turned off disappearing messages. Messages you send will no longer disappear.", - "youSetYourDisappearingMessages": "You set your messages to disappear $time$ after they have been $type$.", - "theyDisabledTheirDisappearingMessages": "$name$ has turned off disappearing messages. Messages they send will no longer disappear.", - "theySetTheirDisappearingMessages": "$name$ has set their messages to disappear $time$ after they have been $type$.", + "disabledDisappearingMessages": "$name$ has turned off disappearing messages.", + "youDisabledDisappearingMessages": "You have turned off disappearing messages.", + "youDisabledYourDisappearingMessages": "You turned off disappearing messages. Messages you send will no longer disappear.", + "youSetYourDisappearingMessages": "You set your messages to disappear $time$ after they have been $type$.", + "theyDisabledTheirDisappearingMessages": "$name$ has turned off disappearing messages. Messages they send will no longer disappear.", + "theySetTheirDisappearingMessages": "$name$ has set their messages to disappear $time$ after they have been $type$.", "timerSetTo": "Disappearing message time set to $time$", "set": "Set", "changeNickname": "Change Nickname", diff --git a/package.json b/package.json index 07d794836..5a981bba9 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "glob": "7.1.2", "image-type": "^4.1.0", "ip2country": "1.0.1", - "libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.0/libsession_util_nodejs-v0.3.0.tar.gz", + "libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.1/libsession_util_nodejs-v0.3.1.tar.gz", "libsodium-wrappers-sumo": "^0.7.9", "linkify-it": "^4.0.1", "lodash": "^4.17.21", diff --git a/ts/components/basic/Text.tsx b/ts/components/basic/Text.tsx index 9e8de730f..fd6e7a879 100644 --- a/ts/components/basic/Text.tsx +++ b/ts/components/basic/Text.tsx @@ -10,7 +10,7 @@ type TextProps = { ellipsisOverflow?: boolean; }; -const StyledDefaultText = styled.div` +const StyledDefaultText = styled.div>` transition: var(--default-duration); max-width: ${props => (props.maxWidth ? props.maxWidth : '')}; padding: ${props => (props.padding ? props.padding : '')}; @@ -26,6 +26,12 @@ export const Text = (props: TextProps) => { return {props.text}; }; +export const TextWithChildren = ( + props: Omit & { children: React.ReactNode } +) => { + return {props.children}; +}; + type SpacerProps = { size: 'xl' | 'lg' | 'md' | 'sm' | 'xs'; style?: CSSProperties; diff --git a/ts/components/conversation/TimerNotification.tsx b/ts/components/conversation/TimerNotification.tsx index 51d77da53..d1abbdd42 100644 --- a/ts/components/conversation/TimerNotification.tsx +++ b/ts/components/conversation/TimerNotification.tsx @@ -17,7 +17,7 @@ import { } from '../../state/selectors/selectedConversation'; import { ReleasedFeatures } from '../../util/releaseFeature'; import { Flex } from '../basic/Flex'; -import { Text } from '../basic/Text'; +import { TextWithChildren } from '../basic/Text'; import { ExpirableReadableMessage } from './message/message-item/ExpirableReadableMessage'; // eslint-disable-next-line import/order import { pick } from 'lodash'; @@ -25,6 +25,7 @@ import { ConversationInteraction } from '../../interactions'; import { getConversationController } from '../../session/conversations'; import { updateConfirmModal } from '../../state/ducks/modalDialog'; import { SessionButtonColor } from '../basic/SessionButton'; +import { SessionHtmlRenderer } from '../basic/SessionHTMLRenderer'; const FollowSettingButton = styled.button` color: var(--primary-color); @@ -208,7 +209,9 @@ export const TimerNotification = (props: PropsForExpirationTimer) => { padding="5px 10px" style={{ textAlign: 'center' }} > - + + + diff --git a/ts/components/conversation/message/message-content/MessageStatus.tsx b/ts/components/conversation/message/message-content/MessageStatus.tsx index ff47ca60e..939e876b4 100644 --- a/ts/components/conversation/message/message-content/MessageStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageStatus.tsx @@ -6,6 +6,7 @@ import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelecto import { useMessageStatus } from '../../../../state/selectors'; import { getMostRecentMessageId } from '../../../../state/selectors/conversations'; +import { useSelectedIsGroup } from '../../../../state/selectors/selectedConversation'; import { SpacerXS } from '../../../basic/Text'; import { SessionIcon, SessionIconType } from '../../../icon'; import { ExpireTimer } from '../../ExpireTimer'; @@ -60,7 +61,7 @@ export const MessageStatus = (props: Props) => { } }; -const MessageStatusContainer = styled.div<{ isIncoming: boolean }>` +const MessageStatusContainer = styled.div<{ isIncoming: boolean; isGroup: boolean }>` display: inline-block; align-self: ${props => (props.isIncoming ? 'flex-start' : 'flex-end')}; flex-direction: ${props => @@ -73,6 +74,8 @@ const MessageStatusContainer = styled.div<{ isIncoming: boolean }>` cursor: pointer; display: flex; align-items: center; + margin-inline-start: ${props => + props.isGroup || !props.isIncoming ? 'var(--width-avatar-group-msg-list)' : 0}; `; const StyledStatusText = styled.div` @@ -144,7 +147,12 @@ function MessageStatusExpireTimer(props: Props) { const MessageStatusSending = ({ dataTestId }: Props) => { // while sending, we do not display the expire timer at all. return ( - + @@ -171,13 +179,19 @@ function IconForExpiringMessageId({ const MessageStatusSent = ({ dataTestId, messageId }: Props) => { const isExpiring = useIsExpiring(messageId); const isMostRecentMessage = useIsMostRecentMessage(messageId); + const isGroup = useSelectedIsGroup(); // we hide a "sent" message status which is not expiring except for the most recent message if (!isExpiring && !isMostRecentMessage) { return null; } return ( - + @@ -190,6 +204,7 @@ const MessageStatusRead = ({ isIncoming, }: Props & { isIncoming: boolean }) => { const isExpiring = useIsExpiring(messageId); + const isGroup = useSelectedIsGroup(); const isMostRecentMessage = useIsMostRecentMessage(messageId); @@ -199,7 +214,12 @@ const MessageStatusRead = ({ } return ( - + @@ -211,6 +231,7 @@ const MessageStatusError = ({ dataTestId }: Props) => { ipcRenderer.send('show-debug-log'); }, []); // when on error, we do not display the expire timer at all. + const isGroup = useSelectedIsGroup(); return ( { onClick={showDebugLog} title={window.i18n('sendFailed')} isIncoming={false} + isGroup={isGroup} > diff --git a/ts/data/data.ts b/ts/data/data.ts index b2b789e28..233ceeb59 100644 --- a/ts/data/data.ts +++ b/ts/data/data.ts @@ -252,6 +252,19 @@ async function saveMessages(arrayOfMessages: Array): Promise< await channels.saveMessages(cleanData(arrayOfMessages)); } +/** + * + * @param conversationId the conversation from which to remove all but the most recent disappear timer update + * @param isPrivate if that conversation is private, we keep a expiration timer update for each sender + * @returns the array of messageIds removed, or [] if none were removed + */ +async function cleanUpExpirationTimerUpdateHistory( + conversationId: string, + isPrivate: boolean +): Promise> { + return channels.cleanUpExpirationTimerUpdateHistory(conversationId, isPrivate); +} + async function removeMessage(id: string): Promise { const message = await getMessageById(id, true); @@ -800,6 +813,7 @@ export const Data = { saveMessages, removeMessage, removeMessagesByIds, + cleanUpExpirationTimerUpdateHistory, getMessageIdsFromServerIds, getMessageById, getMessagesBySenderAndSentAt, diff --git a/ts/data/dataInit.ts b/ts/data/dataInit.ts index 7846a6508..cd2186dfa 100644 --- a/ts/data/dataInit.ts +++ b/ts/data/dataInit.ts @@ -41,6 +41,7 @@ const channelsToMake = new Set([ 'saveMessages', 'removeMessage', 'removeMessagesByIds', + 'cleanUpExpirationTimerUpdateHistory', 'getUnreadByConversation', 'getUnreadDisappearingByConversation', 'markAllAsReadByConversationNoExpiration', diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index a54421db9..df8f620d0 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -48,6 +48,7 @@ import { conversationsChanged, markConversationFullyRead, MessageModelPropsWithoutConvoProps, + messagesDeleted, ReduxConversationType, } from '../state/ducks/conversations'; @@ -823,7 +824,6 @@ export class ConversationModel extends Backbone.Model { fromSync, // if the update comes from a config or sync message fromCurrentDevice, shouldCommitConvo = true, - shouldCommitMessage = true, existingMessage, }: { providedDisappearingMode?: DisappearingMessageConversationModeType; @@ -833,7 +833,6 @@ export class ConversationModel extends Backbone.Model { fromSync: boolean; fromCurrentDevice: boolean; shouldCommitConvo?: boolean; - shouldCommitMessage?: boolean; existingMessage?: MessageModel; }): Promise { const isRemoteChange = Boolean((receivedAt || fromSync) && !fromCurrentDevice); @@ -864,7 +863,6 @@ export class ConversationModel extends Backbone.Model { ); const isV2DisappearReleased = ReleasedFeatures.isDisappearMessageV2FeatureReleasedCached(); - // when the v2 disappear is released, the changes we make are only for our outgoing messages, not shared with a contact anymore if (isV2DisappearReleased) { if (!this.isPrivate()) { @@ -873,6 +871,10 @@ export class ConversationModel extends Backbone.Model { expireTimer, }); } else if (fromSync || fromCurrentDevice) { + if (expirationMode === 'legacy') { + // TODO legacy messages support will be removed in a future release + return false; + } // v2 is live, this is a private chat and a change we made, set the setting to what was given, otherwise discard it this.set({ expirationMode, @@ -960,16 +962,16 @@ export class ConversationModel extends Backbone.Model { message.get('id') ), }); - if (shouldCommitMessage) { - await message.commit(); - } } } - return true; - } - if (shouldCommitMessage) { await message.commit(); + + await cleanUpExpireHistoryFromConvo(this.id, this.isPrivate()); + return true; } + await message.commit(); + + await cleanUpExpireHistoryFromConvo(this.id, this.isPrivate()); // // Below is the "sending the update to the conversation" part. // We would have returned if that message sending part was not needed @@ -1158,18 +1160,18 @@ export class ConversationModel extends Backbone.Model { const sendReceipt = settingsReadReceiptEnabled && !this.isBlocked() && !this.isIncomingRequest(); - if (sendReceipt) { - window?.log?.info(`Sending ${timestamps.length} read receipts.`); - // we should probably stack read receipts and send them every 5 seconds for instance per conversation + if (!sendReceipt) { + return; + } + window?.log?.info(`Sending ${timestamps.length} read receipts.`); - const receiptMessage = new ReadReceiptMessage({ - timestamp: Date.now(), - timestamps, - }); + const receiptMessage = new ReadReceiptMessage({ + timestamp: Date.now(), + timestamps, + }); - const device = new PubKey(this.id); - await getMessageQueue().sendToPubKey(device, receiptMessage, SnodeNamespaces.UserMessages); - } + const device = new PubKey(this.id); + await getMessageQueue().sendToPubKey(device, receiptMessage, SnodeNamespaces.UserMessages); } public async setNickname(nickname: string | null, shouldCommit = false) { @@ -2519,3 +2521,14 @@ export function hasValidIncomingRequestValues({ const isActive = activeAt && isFinite(activeAt) && activeAt > 0; return Boolean(isPrivate && !isMe && !isApproved && !isBlocked && isActive && didApproveMe); } + +async function cleanUpExpireHistoryFromConvo(conversationId: string, isPrivate: boolean) { + const updateIdsRemoved = await Data.cleanUpExpirationTimerUpdateHistory( + conversationId, + isPrivate + ); + + window.inboxStore.dispatch( + messagesDeleted(updateIdsRemoved.map(m => ({ conversationKey: conversationId, messageId: m }))) + ); +} diff --git a/ts/node/migration/sessionMigrations.ts b/ts/node/migration/sessionMigrations.ts index 2da38addd..c66189a4b 100644 --- a/ts/node/migration/sessionMigrations.ts +++ b/ts/node/migration/sessionMigrations.ts @@ -1677,6 +1677,8 @@ function updateToSessionSchemaVersion34(currentVersion: number, db: BetterSqlite // Message changes db.prepare(`ALTER TABLE ${MESSAGES_TABLE} ADD COLUMN expirationType TEXT;`).run(); + db.prepare(`ALTER TABLE ${MESSAGES_TABLE} ADD COLUMN flags INTEGER;`).run(); + db.prepare(`UPDATE ${MESSAGES_TABLE} SET flags = json_extract(json, '$.flags');`); // #endregion diff --git a/ts/node/sql.ts b/ts/node/sql.ts index 4f7ccc979..495539ee9 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -61,6 +61,8 @@ import { } from '../types/sqlSharedTypes'; import { KNOWN_BLINDED_KEYS_ITEM, SettingsKey } from '../data/settings-key'; +import { MessageAttributes } from '../models/messageType'; +import { SignalService } from '../protobuf'; import { Quote } from '../receiver/types'; import { getSQLCipherIntegrityCheck, @@ -780,11 +782,10 @@ function getMessageCount() { return row['count(*)']; } -function saveMessage(data: any) { +function saveMessage(data: MessageAttributes) { const { body, conversationId, - // eslint-disable-next-line camelcase expires_at, hasAttachments, hasFileAttachments, @@ -792,10 +793,8 @@ function saveMessage(data: any) { id, serverId, serverTimestamp, - // eslint-disable-next-line camelcase received_at, sent, - // eslint-disable-next-line camelcase sent_at, source, type, @@ -803,6 +802,7 @@ function saveMessage(data: any) { expirationType, expireTimer, expirationStartTimestamp, + flags, } = data; if (!id) { @@ -834,6 +834,7 @@ function saveMessage(data: any) { source, type: type || '', unread, + flags: flags ?? 0, }; assertGlobalInstance() @@ -857,7 +858,8 @@ function saveMessage(data: any) { sent_at, source, type, - unread + unread, + flags ) values ( $id, $json, @@ -877,7 +879,8 @@ function saveMessage(data: any) { $sent_at, $source, $type, - $unread + $unread, + $flags );` ) .run(payload); @@ -959,8 +962,8 @@ function cleanSeenMessages() { }); } -function saveMessages(arrayOfMessages: Array) { - console.info('saveMessages of length: ', arrayOfMessages.length); +function saveMessages(arrayOfMessages: Array) { + console.info('saveMessages count: ', arrayOfMessages.length); assertGlobalInstance().transaction(() => { map(arrayOfMessages, saveMessage); })(); @@ -969,8 +972,6 @@ function saveMessages(arrayOfMessages: Array) { function removeMessage(id: string, instance?: BetterSqlite3.Database) { if (!isString(id)) { throw new Error('removeMessage: only takes single message to delete!'); - - return; } assertGlobalInstanceOrInstance(instance) @@ -1008,6 +1009,48 @@ function removeAllMessagesInConversation( .run({ conversationId }); } +function cleanUpExpirationTimerUpdateHistory( + conversationId: string, + isPrivate: boolean, + db?: BetterSqlite3.Database +) { + if (isEmpty(conversationId)) { + return []; + } + const rows = assertGlobalInstanceOrInstance(db) + .prepare( + `SELECT id, source FROM ${MESSAGES_TABLE} WHERE conversationId = $conversationId and flags = ${SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE} ${orderByClause}` + ) + .all({ conversationId }); + + if (rows.length <= 1) { + return []; + } + + // we want to allow 1 message at most per sender for private chats only + const bySender: Record> = {}; + // we keep the order, so the first message of each array should be kept, the other ones discarded + rows.forEach(r => { + const groupedById = isPrivate ? r.source : conversationId; + if (!bySender[groupedById]) { + bySender[groupedById] = []; + } + bySender[groupedById].push(r.id); + }); + + const allMsgIdsRemoved: Array = []; + Object.keys(bySender).forEach(k => { + const idsToRemove = bySender[k].slice(1); // we keep the first one + if (isEmpty(idsToRemove)) { + return; + } + removeMessagesByIds(idsToRemove, db); + allMsgIdsRemoved.push(...idsToRemove); + }); + + return allMsgIdsRemoved; +} + function getMessageIdsFromServerIds(serverIds: Array, conversationId: string) { if (!Array.isArray(serverIds)) { return []; @@ -2414,6 +2457,7 @@ export const sqlNode = { saveMessages, removeMessage, removeMessagesByIds, + cleanUpExpirationTimerUpdateHistory, removeAllMessagesInConversation, getUnreadByConversation, getUnreadDisappearingByConversation, diff --git a/ts/receiver/closedGroups.ts b/ts/receiver/closedGroups.ts index a8e29d2ed..06dea961a 100644 --- a/ts/receiver/closedGroups.ts +++ b/ts/receiver/closedGroups.ts @@ -279,7 +279,6 @@ export async function handleNewClosedGroup( const envelopeTimestamp = toNumber(envelope.timestamp); // a type new is sent and received on one to one so do not use envelope.senderIdentity here const sender = envelope.source; - if ( (await sentAtMoreRecentThanWrapper(envelopeTimestamp, 'UserGroupsConfig')) === 'wrapper_more_recent' diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index a67cfde16..8f4baa7d5 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -230,6 +230,7 @@ async function handleUserProfileUpdate(result: IncomingConfResult): Promise 0 && - // a message has started to disappear - foundMessage.getExpirationStartTimestamp() - ) { - const expirationMode = DisappearingMessages.changeToDisappearingConversationMode( - convo, - expirationType, - expireTimer - ); + // If message also has a sync message, save that hash. Otherwise save the hash from the regular message send i.e. only closed groups in this case. - const canBeDeleteAfterRead = convo && !convo.isMe() && convo.isPrivate(); + if ( + encryptedAndWrapped.identifier && + (encryptedAndWrapped.isSyncMessage || isDestinationClosedGroup) + ) { + const foundMessage = await Data.getMessageById(encryptedAndWrapped.identifier); + if (foundMessage) { + await foundMessage.updateMessageHash(storedHash); + const convo = foundMessage.getConversation(); + const expireTimer = foundMessage.getExpireTimer(); + const expirationType = foundMessage.getExpirationType(); - // TODO legacy messages support will be removed in a future release if ( - canBeDeleteAfterRead && - (expirationMode === 'legacy' || expirationMode === 'deleteAfterRead') + convo && + expirationType && + expireTimer > 0 && + // a message has started to disappear + foundMessage.getExpirationStartTimestamp() ) { - await DisappearingMessages.updateMessageExpiryOnSwarm(foundMessage, 'send()'); + const expirationMode = DisappearingMessages.changeToDisappearingConversationMode( + convo, + expirationType, + expireTimer + ); + + const canBeDeleteAfterRead = convo && !convo.isMe() && convo.isPrivate(); + + // TODO legacy messages support will be removed in a future release + if ( + canBeDeleteAfterRead && + (expirationMode === 'legacy' || expirationMode === 'deleteAfterRead') + ) { + await DisappearingMessages.updateMessageExpiryOnSwarm(foundMessage, 'send()'); + } } - } - await foundMessage.commit(); + await foundMessage.commit(); + } } } diff --git a/ts/session/utils/libsession/libsession_utils_user_profile.ts b/ts/session/utils/libsession/libsession_utils_user_profile.ts index 09785c2b8..2d81c12b4 100644 --- a/ts/session/utils/libsession/libsession_utils_user_profile.ts +++ b/ts/session/utils/libsession/libsession_utils_user_profile.ts @@ -26,7 +26,6 @@ async function insertUserProfileIntoWrapper(convoId: string) { const areBlindedMsgRequestEnabled = !!Storage.get(SettingsKey.hasBlindedMsgRequestsEnabled); const expirySeconds = ourConvo.getExpireTimer() || 0; - window.log.debug( `inserting into userprofile wrapper: username:"${dbName}", priority:${priority} image:${JSON.stringify( { diff --git a/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts b/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts index 88aeabc10..9f86fc61a 100644 --- a/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts +++ b/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts @@ -600,7 +600,6 @@ describe('DisappearingMessage', () => { receivedAt: GetNetworkTime.getNowWithNetworkOffset(), fromSync: true, shouldCommitConvo: false, - shouldCommitMessage: false, existingMessage: undefined, fromCurrentDevice: false, }); diff --git a/yarn.lock b/yarn.lock index 0225d629f..ff41fe0d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1039,21 +1039,21 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react@*", "@types/react@^17", "@types/react@^17.0.2": - version "17.0.65" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.65.tgz#95f6a2ab61145ffb69129d07982d047f9e0870cd" - integrity sha512-oxur785xZYHvnI7TRS61dXbkIhDPnGfsXKv0cNXR/0ml4SipRIFpSMzA7HMEfOywFwJ5AOnPrXYTEiTRUQeGlQ== +"@types/react@*", "@types/react@17.0.2", "@types/react@^17": + version "17.0.2" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8" + integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@17.0.2": - version "17.0.2" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8" - integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA== +"@types/react@^17.0.2": + version "17.0.65" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.65.tgz#95f6a2ab61145ffb69129d07982d047f9e0870cd" + integrity sha512-oxur785xZYHvnI7TRS61dXbkIhDPnGfsXKv0cNXR/0ml4SipRIFpSMzA7HMEfOywFwJ5AOnPrXYTEiTRUQeGlQ== dependencies: "@types/prop-types" "*" + "@types/scheduler" "*" csstype "^3.0.2" "@types/redux-logger@3.0.7": @@ -1817,9 +1817,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== axios@^1.3.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f" - integrity sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A== + version "1.6.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" + integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -4896,9 +4896,9 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.0/libsession_util_nodejs-v0.3.0.tar.gz": - version "0.3.0" - resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.0/libsession_util_nodejs-v0.3.0.tar.gz#83b733c8fdede577651881de239a8fd2843c929f" +"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.1/libsession_util_nodejs-v0.3.1.tar.gz": + version "0.3.1" + resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.1/libsession_util_nodejs-v0.3.1.tar.gz#d2c94bfaae6e3ef594609abb08cf8be485fa5d39" dependencies: cmake-js "^7.2.1" node-addon-api "^6.1.0"