diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index d498763cd..36ebc5239 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -578,6 +578,8 @@
"respondingToGroupRequestWarning": "Sending a message to this group will automatically accept the group invite.",
"userInvitedYouToGroup": "$name$ invited you to join $groupName$.",
"youWereInvitedToGroup": "You were invited to join $groupName$.",
+ "userInvitedYouToGroupAsAdmin": "$name$ invited you to join $groupName$, where you are an Admin.",
+ "youWereInvitedToGroupAsAdmin": "You were invited to join $groupName$, where you are an Admin.",
"hideRequestBanner": "Hide Message Request Banner",
"openMessageRequestInbox": "Message Requests",
"noMessageRequestsPending": "No pending message requests",
diff --git a/package.json b/package.json
index 5823f6b4e..0f481a6a5 100644
--- a/package.json
+++ b/package.json
@@ -95,7 +95,7 @@
"fs-extra": "9.0.0",
"glob": "10.3.10",
"image-type": "^4.1.0",
- "libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.19/libsession_util_nodejs-v0.3.19.tar.gz",
+ "libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.21/libsession_util_nodejs-v0.3.21.tar.gz",
"libsodium-wrappers-sumo": "^0.7.9",
"linkify-it": "^4.0.1",
"lodash": "^4.17.21",
diff --git a/ts/components/conversation/SessionConversation.tsx b/ts/components/conversation/SessionConversation.tsx
index 73b789059..1f58320fe 100644
--- a/ts/components/conversation/SessionConversation.tsx
+++ b/ts/components/conversation/SessionConversation.tsx
@@ -6,6 +6,7 @@ import { blobToArrayBuffer } from 'blob-util';
import loadImage from 'blueimp-load-image';
import classNames from 'classnames';
import styled from 'styled-components';
+import { useDispatch } from 'react-redux';
import {
CompositionBox,
SendMessageType,
@@ -60,7 +61,6 @@ import { SessionSpinner } from '../basic/SessionSpinner';
import { ConversationMessageRequestButtons } from './MessageRequestButtons';
import { RightPanel, StyledRightPanelContainer } from './right-panel/RightPanel';
import { showLinkVisitWarningDialog } from '../dialog/SessionConfirm';
-import { useDispatch } from 'react-redux';
const DEFAULT_JPEG_QUALITY = 0.85;
interface State {
diff --git a/ts/components/conversation/SubtleNotification.tsx b/ts/components/conversation/SubtleNotification.tsx
index be4e50bf8..02840cb98 100644
--- a/ts/components/conversation/SubtleNotification.tsx
+++ b/ts/components/conversation/SubtleNotification.tsx
@@ -25,6 +25,7 @@ import {
useLibGroupInviteGroupName,
useLibGroupInvitePending,
useLibGroupKicked,
+ useLibGroupWeHaveSecretKey,
} from '../../state/selectors/userGroups';
import { LocalizerKeys } from '../../types/LocalizerKeys';
import { SessionHtmlRenderer } from '../basic/SessionHTMLRenderer';
@@ -119,6 +120,8 @@ export const InvitedToGroupControlMessage = () => {
const adminNameInvitedUs =
useNicknameOrProfileNameOrShortenedPubkey(conversationOrigin) || window.i18n('unknown');
const isGroupPendingInvite = useLibGroupInvitePending(selectedConversation);
+ const weHaveSecretKey = useLibGroupWeHaveSecretKey(selectedConversation);
+
if (
!selectedConversation ||
isApproved ||
@@ -131,8 +134,12 @@ export const InvitedToGroupControlMessage = () => {
}
// when restoring from seed we might not have the pubkey of who invited us, in that case, we just use a fallback
const html = conversationOrigin
- ? window.i18n('userInvitedYouToGroup', [adminNameInvitedUs, groupName])
- : window.i18n('youWereInvitedToGroup', [groupName]);
+ ? weHaveSecretKey
+ ? window.i18n('userInvitedYouToGroupAsAdmin', [adminNameInvitedUs, groupName])
+ : window.i18n('userInvitedYouToGroup', [adminNameInvitedUs, groupName])
+ : weHaveSecretKey
+ ? window.i18n('youWereInvitedToGroupAsAdmin', [groupName])
+ : window.i18n('youWereInvitedToGroup', [groupName]);
return (
{
data-testid="message-input-text-area"
style={style}
suggestionsPortalHost={this.container as any}
- forceSuggestionsAboveCursor={true} // force mentions to be rendered on top of the cursor, this is working with a fork of react-mentions for now
+ forceSuggestionsAboveCursor={true}
>
{
{hasContacts && isGroupV2 && }
- {isGroupV2 && (
+
+ {/* TODO: localize those strings once out releasing those buttons for real */}
+ {isGroupV2 && isDevProd() && (
<>
Share History?{' '}
diff --git a/ts/components/dialog/SessionConfirm.tsx b/ts/components/dialog/SessionConfirm.tsx
index 32e0d8a2b..c171dfd55 100644
--- a/ts/components/dialog/SessionConfirm.tsx
+++ b/ts/components/dialog/SessionConfirm.tsx
@@ -219,17 +219,11 @@ export const SessionConfirm = (props: SessionConfirmDialogProps) => {
);
};
-
-
-
-
export const showLinkVisitWarningDialog = (urlToOpen: string, dispatch: Dispatch) => {
function onClickOk() {
void shell.openExternal(urlToOpen);
}
-
-
dispatch(
updateConfirmModal({
title: window.i18n('linkVisitWarningTitle'),
diff --git a/ts/components/leftpane/ActionsPanel.tsx b/ts/components/leftpane/ActionsPanel.tsx
index cbd1f3fcb..fef26e3b5 100644
--- a/ts/components/leftpane/ActionsPanel.tsx
+++ b/ts/components/leftpane/ActionsPanel.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-await-in-loop */
import { ipcRenderer } from 'electron';
import React, { useEffect, useState } from 'react';
diff --git a/ts/components/leftpane/overlay/OverlayClosedGroup.tsx b/ts/components/leftpane/overlay/OverlayClosedGroup.tsx
index fe8db6d1b..03569a6dd 100644
--- a/ts/components/leftpane/overlay/OverlayClosedGroup.tsx
+++ b/ts/components/leftpane/overlay/OverlayClosedGroup.tsx
@@ -5,6 +5,7 @@ import useKey from 'react-use/lib/useKey';
import styled from 'styled-components';
import { concat } from 'lodash';
+import useUpdate from 'react-use/lib/useUpdate';
import { MemberListItem } from '../../MemberListItem';
import { SessionButton } from '../../basic/SessionButton';
import { SessionIdEditable } from '../../basic/SessionIdEditable';
@@ -24,6 +25,8 @@ import { useOurPkStr } from '../../../state/selectors/user';
import { SessionSearchInput } from '../../SessionSearchInput';
import { SpacerLG } from '../../basic/Text';
import { GroupInviteRequiredVersionBanner } from '../../NoticeBanner';
+import { isDevProd } from '../../../shared/env_vars';
+import { SessionToggle } from '../../basic/SessionToggle';
const StyledMemberListNoContacts = styled.div`
font-family: var(--font-mono), var(--font-default);
@@ -100,6 +103,7 @@ export const OverlayClosedGroupV2 = () => {
const privateContactsPubkeys = useContactsToInviteToGroup();
const isCreatingGroup = useIsCreatingGroupFromUIPending();
const [groupName, setGroupName] = useState('');
+ const forceUpdate = useUpdate();
const {
uniqueValues: members,
addTo: addToSelected,
@@ -177,6 +181,22 @@ export const OverlayClosedGroupV2 = () => {
/>
+ {/* TODO: localize those strings once out releasing those buttons for real */}
+ {isDevProd() && (
+ <>
+
+ Invite as admin?{' '}
+ {
+ window.sessionFeatureFlags.useGroupV2InviteAsAdmin =
+ !window.sessionFeatureFlags.useGroupV2InviteAsAdmin;
+ forceUpdate();
+ }}
+ />
+
+ >
+ )}
{!noContactsForClosedGroup && window.sessionFeatureFlags.useClosedGroupV2 && (
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts
index 7e86e7387..4860e1c61 100644
--- a/ts/models/conversation.ts
+++ b/ts/models/conversation.ts
@@ -1101,7 +1101,7 @@ export class ConversationModel extends Backbone.Model {
if (this.isClosedGroup()) {
if (this.isAdmin(UserUtils.getOurPubKeyStrFromCache())) {
if (this.isClosedGroupV2()) {
- if (!PubKey.is03Pubkey(this.id)) {
+ if (!PubKey.is03Pubkey(this.id)) {
throw new Error('updateExpireTimer v2 group requires a 03 key');
}
const group = await UserGroupsWrapperActions.getGroup(this.id);
diff --git a/ts/receiver/groupv2/handleGroupV2Message.ts b/ts/receiver/groupv2/handleGroupV2Message.ts
index 38d2c5a7d..88f45c170 100644
--- a/ts/receiver/groupv2/handleGroupV2Message.ts
+++ b/ts/receiver/groupv2/handleGroupV2Message.ts
@@ -1,6 +1,7 @@
import { GroupPubkeyType, PubkeyType, WithGroupPubkey } from 'libsession_util_nodejs';
import { compact, isEmpty, isFinite, isNumber } from 'lodash';
import { Data } from '../../data/data';
+import { deleteAllMessagesByConvoIdNoConfirmation } from '../../interactions/conversationInteractions';
import { deleteMessagesFromSwarmOnly } from '../../interactions/conversations/unsendingInteractions';
import { ConversationTypeEnum } from '../../models/conversationAttributes';
import { HexString } from '../../node/hexStrings';
@@ -14,6 +15,7 @@ import { WithDisappearingMessageUpdate } from '../../session/disappearing_messag
import { ClosedGroup } from '../../session/group/closed-group';
import { GroupUpdateInviteResponseMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_group/GroupUpdateInviteResponseMessage';
import { PubKey } from '../../session/types';
+import { WithMessageHash } from '../../session/types/with';
import { UserUtils } from '../../session/utils';
import { sleepFor } from '../../session/utils/Promise';
import { ed25519Str, stringToUint8Array } from '../../session/utils/String';
@@ -29,8 +31,6 @@ import {
MetaGroupWrapperActions,
UserGroupsWrapperActions,
} from '../../webworker/workers/browser/libsession_worker_interface';
-import { WithMessageHash } from '../../session/types/with';
-import { deleteAllMessagesByConvoIdNoConfirmation } from '../../interactions/conversationInteractions';
type WithSignatureTimestamp = { signatureTimestamp: number };
type WithAuthor = { author: PubkeyType };
@@ -503,41 +503,109 @@ async function handleGroupUpdateInviteResponseMessage({
async function handleGroupUpdatePromoteMessage({
change,
+ author,
+ signatureTimestamp,
}: Omit, 'groupPk'>) {
const seed = change.groupIdentitySeed;
const sodium = await getSodiumRenderer();
const groupKeypair = sodium.crypto_sign_seed_keypair(seed);
const groupPk = `03${HexString.toHexString(groupKeypair.publicKey)}` as GroupPubkeyType;
+ // we can be invited via a GroupUpdatePromoteMessage as an admin right away,
+ // so we potentially need to deal with part of the invite process here too.
- const convo = ConvoHub.use().get(groupPk);
- if (!convo) {
+ if (BlockedNumberController.isBlocked(author)) {
+ window.log.info(
+ `received promote to group ${ed25519Str(groupPk)} by blocked user:${ed25519Str(
+ author
+ )}... dropping it`
+ );
return;
}
- window.log.info(`handleGroupUpdatePromoteMessage for ${ed25519Str(groupPk)}`);
- // no group update message here, another message is sent to the group's swarm for the update message.
- // this message is just about the keys that we need to save, and accepting the promotion.
+ const authorIsApproved = ConvoHub.use().get(author)?.isApproved() || false;
+ window.log.info(
+ `received promote to group ${ed25519Str(groupPk)} by author:${ed25519Str(author)}. authorIsApproved:${authorIsApproved} `
+ );
- const found = await UserGroupsWrapperActions.getGroup(groupPk);
+ const convo = await ConvoHub.use().getOrCreateAndWait(groupPk, ConversationTypeEnum.GROUPV2);
+ convo.set({
+ active_at: signatureTimestamp,
+ didApproveMe: true,
+ conversationIdOrigin: author,
+ });
+
+ if (change.name && isEmpty(convo.getRealSessionUsername())) {
+ convo.set({
+ displayNameInProfile: change.name,
+ });
+ }
+ const userEd25519Secretkey = (await UserUtils.getUserED25519KeyPairBytes()).privKeyBytes;
+
+ let found = await UserGroupsWrapperActions.getGroup(groupPk);
+ const wasKicked = found?.kicked || false;
if (!found) {
- // could have been removed by the user already so let's not force create it
- window.log.info(
- 'received group promote message but that group is not in the usergroups wrapper'
- );
- return;
+ found = {
+ authData: null,
+ joinedAtSeconds: Date.now(),
+ name: change.name,
+ priority: 0,
+ pubkeyHex: groupPk,
+ secretKey: groupKeypair.privateKey,
+ kicked: false,
+ invitePending: true,
+ };
+ } else {
+ found.kicked = false;
+ found.name = change.name;
+ found.secretKey = groupKeypair.privateKey;
+ }
+ if (authorIsApproved) {
+ // pre approve invite to groups when we've already approved the person who invited us
+ found.invitePending = false;
}
- found.secretKey = groupKeypair.privateKey;
+
await UserGroupsWrapperActions.setGroup(found);
- await UserSync.queueNewJobIfNeeded();
+ // force markedAsUnread to be true so it shows the unread banner (we only show the banner if there are unread messages on at least one msg/group request)
+ await convo.markAsUnread(true, false);
+ await convo.commit();
- window.inboxStore.dispatch(
- groupInfoActions.markUsAsAdmin({
- groupPk,
- secret: groupKeypair.privateKey,
- })
- );
+ await SessionUtilConvoInfoVolatile.insertConvoFromDBIntoWrapperAndRefresh(convo.id);
+
+ if (wasKicked) {
+ // we have been reinvited to a group which we had been kicked from.
+ // Let's empty the conversation again to remove any "you were removed from the group" control message
+ await deleteAllMessagesByConvoIdNoConfirmation(groupPk);
+ }
+ try {
+ await MetaGroupWrapperActions.init(groupPk, {
+ metaDumped: null,
+ groupEd25519Secretkey: groupKeypair.privateKey,
+ userEd25519Secretkey: toFixedUint8ArrayOfLength(userEd25519Secretkey, 64).buffer,
+ groupEd25519Pubkey: toFixedUint8ArrayOfLength(HexString.fromHexStringNoPrefix(groupPk), 32)
+ .buffer,
+ });
+ } catch (e) {
+ window.log.warn(
+ `handleGroupUpdatePromoteMessage: init of ${ed25519Str(groupPk)} failed with ${e.message}. Trying to just load admin keys`
+ );
+ try {
+ await MetaGroupWrapperActions.loadAdminKeys(groupPk, groupKeypair.privateKey);
+ } catch (e2) {
+ window.log.warn(
+ `handleGroupUpdatePromoteMessage: loadAdminKeys of ${ed25519Str(groupPk)} failed with ${e.message}`
+ );
+ }
+ }
+
+ await LibSessionUtil.saveDumpsToDb(UserUtils.getOurPubKeyStrFromCache());
+ await UserSync.queueNewJobIfNeeded();
+ if (!found.invitePending) {
+ // This group should already be polling based on if that author is pre-approved or we've already approved that group from another device.
+ // Start polling from it, we will mark ourselves as admin once we get the first merge result, if needed.
+ getSwarmPollingInstance().addGroupId(groupPk);
+ }
}
async function handle1o1GroupUpdateMessage(
diff --git a/ts/session/apis/snode_api/signature/groupSignature.ts b/ts/session/apis/snode_api/signature/groupSignature.ts
index 3264dafd5..30ac61e71 100644
--- a/ts/session/apis/snode_api/signature/groupSignature.ts
+++ b/ts/session/apis/snode_api/signature/groupSignature.ts
@@ -61,12 +61,12 @@ async function getGroupPromoteMessage({
member,
secretKey,
groupPk,
- name,
+ groupName,
}: {
member: PubkeyType;
secretKey: Uint8ArrayLen64; // len 64
groupPk: GroupPubkeyType;
- name: string;
+ groupName: string;
}) {
const createAtNetworkTimestamp = GetNetworkTime.now();
@@ -80,7 +80,7 @@ async function getGroupPromoteMessage({
groupIdentitySeed: secretKey.slice(0, 32), // the seed is the first 32 bytes of the secretkey
expirationType: 'unknown', // a promote message is not expiring
expireTimer: 0,
- name,
+ groupName,
});
return msg;
}
diff --git a/ts/session/apis/snode_api/swarm_polling_config/SwarmPollingGroupConfig.ts b/ts/session/apis/snode_api/swarm_polling_config/SwarmPollingGroupConfig.ts
index 0d2e9c220..6911bf2ee 100644
--- a/ts/session/apis/snode_api/swarm_polling_config/SwarmPollingGroupConfig.ts
+++ b/ts/session/apis/snode_api/swarm_polling_config/SwarmPollingGroupConfig.ts
@@ -15,6 +15,8 @@ import { SnodeNamespaces } from '../namespaces';
import { RetrieveMessageItemWithNamespace } from '../types';
import { ConvoHub } from '../../../conversations';
import { ProfileManager } from '../../../profile_manager/ProfileManager';
+import { UserUtils } from '../../../utils';
+import { GroupSync } from '../../../utils/job_runners/jobs/GroupSyncJob';
/**
* This is a basic optimization to avoid running the logic when the `deleteBeforeSeconds`
@@ -36,74 +38,105 @@ async function handleMetaMergeResults(groupPk: GroupPubkeyType) {
to_hex(dumps)
);
}
- if (infos) {
- if (infos.isDestroyed) {
- window.log.info(`${ed25519Str(groupPk)} is marked as destroyed after merge. Removing it.`);
- await ConvoHub.use().deleteGroup(groupPk, {
- sendLeaveMessage: false,
- fromSyncMessage: false,
- emptyGroupButKeepAsKicked: true, // we just got something from the group's swarm, so it is not pendingInvite
- deleteAllMessagesOnSwarm: false,
- forceDestroyForAllMembers: false,
+ if (infos.isDestroyed) {
+ window.log.info(`${ed25519Str(groupPk)} is marked as destroyed after merge. Removing it.`);
+ await ConvoHub.use().deleteGroup(groupPk, {
+ sendLeaveMessage: false,
+ fromSyncMessage: false,
+ emptyGroupButKeepAsKicked: true, // we just got something from the group's swarm, so it is not pendingInvite
+ deleteAllMessagesOnSwarm: false,
+ forceDestroyForAllMembers: false,
+ });
+ } else {
+ if (
+ isNumber(infos.deleteBeforeSeconds) &&
+ isFinite(infos.deleteBeforeSeconds) &&
+ infos.deleteBeforeSeconds > 0 &&
+ (lastAppliedRemoveMsgSentBeforeSeconds.get(groupPk) || Number.MAX_SAFE_INTEGER) >
+ infos.deleteBeforeSeconds
+ ) {
+ // delete any messages in this conversation sent before that timestamp (in seconds)
+ const deletedMsgIds = await Data.removeAllMessagesInConversationSentBefore({
+ deleteBeforeSeconds: infos.deleteBeforeSeconds,
+ conversationId: groupPk,
});
- } else {
- if (
- isNumber(infos.deleteBeforeSeconds) &&
- isFinite(infos.deleteBeforeSeconds) &&
- infos.deleteBeforeSeconds > 0 &&
- (lastAppliedRemoveMsgSentBeforeSeconds.get(groupPk) || Number.MAX_SAFE_INTEGER) >
- infos.deleteBeforeSeconds
- ) {
- // delete any messages in this conversation sent before that timestamp (in seconds)
- const deletedMsgIds = await Data.removeAllMessagesInConversationSentBefore({
- deleteBeforeSeconds: infos.deleteBeforeSeconds,
- conversationId: groupPk,
- });
- window.log.info(
- `removeAllMessagesInConversationSentBefore of ${ed25519Str(groupPk)} before ${infos.deleteBeforeSeconds}: `,
- deletedMsgIds
- );
- window.inboxStore.dispatch(
- messagesExpired(deletedMsgIds.map(messageId => ({ conversationKey: groupPk, messageId })))
- );
- lastAppliedRemoveMsgSentBeforeSeconds.set(groupPk, infos.deleteBeforeSeconds);
- }
+ window.log.info(
+ `removeAllMessagesInConversationSentBefore of ${ed25519Str(groupPk)} before ${infos.deleteBeforeSeconds}: `,
+ deletedMsgIds
+ );
+ window.inboxStore.dispatch(
+ messagesExpired(deletedMsgIds.map(messageId => ({ conversationKey: groupPk, messageId })))
+ );
+ lastAppliedRemoveMsgSentBeforeSeconds.set(groupPk, infos.deleteBeforeSeconds);
+ }
+
+ if (
+ isNumber(infos.deleteAttachBeforeSeconds) &&
+ isFinite(infos.deleteAttachBeforeSeconds) &&
+ infos.deleteAttachBeforeSeconds > 0 &&
+ (lastAppliedRemoveAttachmentSentBeforeSeconds.get(groupPk) || Number.MAX_SAFE_INTEGER) >
+ infos.deleteAttachBeforeSeconds
+ ) {
+ // delete any attachments in this conversation sent before that timestamp (in seconds)
+ const impactedMsgModels = await Data.getAllMessagesWithAttachmentsInConversationSentBefore({
+ deleteAttachBeforeSeconds: infos.deleteAttachBeforeSeconds,
+ conversationId: groupPk,
+ });
+ window.log.info(
+ `getAllMessagesWithAttachmentsInConversationSentBefore of ${ed25519Str(groupPk)} before ${infos.deleteAttachBeforeSeconds}: impactedMsgModelsIds `,
+ impactedMsgModels.map(m => m.id)
+ );
- if (
- isNumber(infos.deleteAttachBeforeSeconds) &&
- isFinite(infos.deleteAttachBeforeSeconds) &&
- infos.deleteAttachBeforeSeconds > 0 &&
- (lastAppliedRemoveAttachmentSentBeforeSeconds.get(groupPk) || Number.MAX_SAFE_INTEGER) >
- infos.deleteAttachBeforeSeconds
- ) {
- // delete any attachments in this conversation sent before that timestamp (in seconds)
- const impactedMsgModels = await Data.getAllMessagesWithAttachmentsInConversationSentBefore({
- deleteAttachBeforeSeconds: infos.deleteAttachBeforeSeconds,
- conversationId: groupPk,
- });
- window.log.info(
- `getAllMessagesWithAttachmentsInConversationSentBefore of ${ed25519Str(groupPk)} before ${infos.deleteAttachBeforeSeconds}: impactedMsgModelsIds `,
- impactedMsgModels.map(m => m.id)
- );
-
- for (let index = 0; index < impactedMsgModels.length; index++) {
- const msg = impactedMsgModels[index];
-
- // eslint-disable-next-line no-await-in-loop
- await msg?.cleanup();
- }
- lastAppliedRemoveAttachmentSentBeforeSeconds.set(groupPk, infos.deleteAttachBeforeSeconds);
+ for (let index = 0; index < impactedMsgModels.length; index++) {
+ const msg = impactedMsgModels[index];
+
+ // eslint-disable-next-line no-await-in-loop
+ await msg?.cleanup();
}
+ lastAppliedRemoveAttachmentSentBeforeSeconds.set(groupPk, infos.deleteAttachBeforeSeconds);
}
- const membersWithPendingRemovals =
- await MetaGroupWrapperActions.memberGetAllPendingRemovals(groupPk);
- if (membersWithPendingRemovals.length) {
- const group = await UserGroupsWrapperActions.getGroup(groupPk);
- if (group && group.secretKey && !isEmpty(group.secretKey)) {
- await GroupPendingRemovals.addJob({ groupPk });
- }
+ }
+ const membersWithPendingRemovals =
+ await MetaGroupWrapperActions.memberGetAllPendingRemovals(groupPk);
+ if (membersWithPendingRemovals.length) {
+ const group = await UserGroupsWrapperActions.getGroup(groupPk);
+ if (group && group.secretKey && !isEmpty(group.secretKey)) {
+ await GroupPendingRemovals.addJob({ groupPk });
}
}
+
+ const us = UserUtils.getOurPubKeyStrFromCache();
+ const usMember = await MetaGroupWrapperActions.memberGet(groupPk, us);
+ let keysAlreadyHaveAdmin = await MetaGroupWrapperActions.keysAdmin(groupPk);
+ const secretKeyInUserWrapper = (await UserGroupsWrapperActions.getGroup(groupPk))?.secretKey;
+
+ // load admin keys if needed
+ if (
+ usMember &&
+ secretKeyInUserWrapper &&
+ !isEmpty(secretKeyInUserWrapper) &&
+ !keysAlreadyHaveAdmin
+ ) {
+ try {
+ await MetaGroupWrapperActions.loadAdminKeys(groupPk, secretKeyInUserWrapper);
+ keysAlreadyHaveAdmin = await MetaGroupWrapperActions.keysAdmin(groupPk);
+ } catch (e) {
+ window.log.warn(
+ `tried to update our adminKeys/state for group ${ed25519Str(groupPk)} but failed with, ${e.message}`
+ );
+ }
+ }
+ // mark ourselves as accepting the invite if needed
+ if (usMember?.invitePending && keysAlreadyHaveAdmin) {
+ await MetaGroupWrapperActions.memberSetAccepted(groupPk, us);
+ }
+ // mark ourselves as accepting the promotion if needed
+ if (usMember?.promotionPending && keysAlreadyHaveAdmin) {
+ await MetaGroupWrapperActions.memberSetPromotionAccepted(groupPk, us);
+ }
+ // this won't do anything if there is no need for a sync, so we can safely plan one
+ await GroupSync.queueNewJobIfNeeded(groupPk);
+
const convo = ConvoHub.use().get(groupPk);
const refreshedInfos = await MetaGroupWrapperActions.infoGet(groupPk);
diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts
index b2c15c5b2..9892cdd94 100644
--- a/ts/session/conversations/ConversationController.ts
+++ b/ts/session/conversations/ConversationController.ts
@@ -319,7 +319,7 @@ class ConvoController {
const us = UserUtils.getOurPubKeyStrFromCache();
const allMembers = await MetaGroupWrapperActions.memberGetAll(groupPk);
const otherAdminsCount = allMembers
- .filter(m => m.admin || m.promoted)
+ .filter(m => m.promoted)
.filter(m => m.pubkeyHex !== us).length;
const weAreLastAdmin = otherAdminsCount === 0;
const infos = await MetaGroupWrapperActions.infoGet(groupPk);
diff --git a/ts/session/messages/outgoing/controlMessage/group_v2/to_user/GroupUpdatePromoteMessage.ts b/ts/session/messages/outgoing/controlMessage/group_v2/to_user/GroupUpdatePromoteMessage.ts
index 5c5c20283..312af61a3 100644
--- a/ts/session/messages/outgoing/controlMessage/group_v2/to_user/GroupUpdatePromoteMessage.ts
+++ b/ts/session/messages/outgoing/controlMessage/group_v2/to_user/GroupUpdatePromoteMessage.ts
@@ -5,7 +5,7 @@ import { GroupUpdateMessage, GroupUpdateMessageParams } from '../GroupUpdateMess
interface Params extends GroupUpdateMessageParams {
groupPk: GroupPubkeyType;
groupIdentitySeed: Uint8Array;
- name: string;
+ groupName: string;
}
/**
@@ -13,17 +13,17 @@ interface Params extends GroupUpdateMessageParams {
*/
export class GroupUpdatePromoteMessage extends GroupUpdateMessage {
public readonly groupIdentitySeed: Params['groupIdentitySeed'];
- public readonly name: Params['name'];
+ public readonly groupName: Params['groupName'];
constructor(params: Params) {
super(params);
this.groupIdentitySeed = params.groupIdentitySeed;
- this.name = params.name;
+ this.groupName = params.groupName;
if (!this.groupIdentitySeed || this.groupIdentitySeed.length !== 32) {
throw new Error('groupIdentitySeed must be set');
}
- if (!this.name) {
+ if (!this.groupName) {
throw new Error('name must be set and not empty');
}
}
@@ -31,7 +31,7 @@ export class GroupUpdatePromoteMessage extends GroupUpdateMessage {
public dataProto(): SignalService.DataMessage {
const promoteMessage = new SignalService.GroupUpdatePromoteMessage({
groupIdentitySeed: this.groupIdentitySeed,
- name: this.name,
+ name: this.groupName,
});
return new SignalService.DataMessage({
diff --git a/ts/session/onions/onionSend.ts b/ts/session/onions/onionSend.ts
index ccef16b06..20c57e73a 100644
--- a/ts/session/onions/onionSend.ts
+++ b/ts/session/onions/onionSend.ts
@@ -17,7 +17,6 @@ import {
FinalRelayOptions,
Onions,
STATUS_NO_STATUS,
- SnodeResponse,
buildErrorMessageWithFailedCode,
} from '../apis/snode_api/onions';
import { PROTOCOLS } from '../constants';
@@ -75,12 +74,6 @@ const getOnionPathForSending = async () => {
return pathNodes;
};
-export type OnionSnodeResponse = {
- result: SnodeResponse;
- txtResponse: string;
- response: string;
-};
-
export type OnionV4SnodeResponse = {
body: string | object | null; // if the content can be decoded as string
bodyBinary: Uint8Array | null; // otherwise we return the raw content (could be an image data or file from sogs/fileserver)
diff --git a/ts/session/utils/job_runners/jobs/GroupInviteJob.ts b/ts/session/utils/job_runners/jobs/GroupInviteJob.ts
index 9fb8aba10..f26de93a3 100644
--- a/ts/session/utils/job_runners/jobs/GroupInviteJob.ts
+++ b/ts/session/utils/job_runners/jobs/GroupInviteJob.ts
@@ -153,12 +153,19 @@ class GroupInviteJob extends PersistedJob {
}
let failed = true;
try {
- const inviteDetails = await SnodeGroupSignature.getGroupInviteMessage({
- groupName: group.name,
- member,
- secretKey: group.secretKey,
- groupPk,
- });
+ const inviteDetails = window.sessionFeatureFlags.useGroupV2InviteAsAdmin
+ ? await SnodeGroupSignature.getGroupPromoteMessage({
+ groupName: group.name,
+ member,
+ secretKey: group.secretKey,
+ groupPk,
+ })
+ : await SnodeGroupSignature.getGroupInviteMessage({
+ groupName: group.name,
+ member,
+ secretKey: group.secretKey,
+ groupPk,
+ });
const storedAt = await getMessageQueue().sendTo1o1NonDurably({
message: inviteDetails,
diff --git a/ts/session/utils/job_runners/jobs/GroupPendingRemovalsJob.ts b/ts/session/utils/job_runners/jobs/GroupPendingRemovalsJob.ts
index b8cb17aa8..b40d6d42e 100644
--- a/ts/session/utils/job_runners/jobs/GroupPendingRemovalsJob.ts
+++ b/ts/session/utils/job_runners/jobs/GroupPendingRemovalsJob.ts
@@ -134,7 +134,7 @@ class GroupPendingRemovalsJob extends PersistedJob m.removedStatus === 2)
+ .filter(m => m.shouldRemoveMessages)
.map(m => m.pubkeyHex);
const sessionIdsHex = pendingRemovals.map(m => m.pubkeyHex);
diff --git a/ts/session/utils/job_runners/jobs/GroupPromoteJob.ts b/ts/session/utils/job_runners/jobs/GroupPromoteJob.ts
index 9ae1675d2..0c8ccd4e7 100644
--- a/ts/session/utils/job_runners/jobs/GroupPromoteJob.ts
+++ b/ts/session/utils/job_runners/jobs/GroupPromoteJob.ts
@@ -102,7 +102,7 @@ class GroupPromoteJob extends PersistedJob {
member,
secretKey: group.secretKey,
groupPk,
- name: group.name,
+ groupName: group.name,
});
const storedAt = await getMessageQueue().sendTo1o1NonDurably({
@@ -118,7 +118,11 @@ class GroupPromoteJob extends PersistedJob {
groupInfoActions.setPromotionPending({ groupPk, pubkey: member, sending: false })
);
try {
- await MetaGroupWrapperActions.memberSetPromoted(groupPk, member, failed);
+ if (failed) {
+ await MetaGroupWrapperActions.memberSetPromotionFailed(groupPk, member);
+ } else {
+ await MetaGroupWrapperActions.memberSetPromotionSent(groupPk, member);
+ }
} catch (e) {
window.log.warn('GroupPromoteJob memberSetPromoted failed with', e.message);
}
diff --git a/ts/session/utils/libsession/libsession_utils.ts b/ts/session/utils/libsession/libsession_utils.ts
index f649e94d6..024470596 100644
--- a/ts/session/utils/libsession/libsession_utils.ts
+++ b/ts/session/utils/libsession/libsession_utils.ts
@@ -410,7 +410,7 @@ async function createMemberAndSetDetails({
await MetaGroupWrapperActions.memberConstructAndSet(groupPk, memberPubkey);
if (displayName) {
- await MetaGroupWrapperActions.memberSetName(groupPk, memberPubkey, displayName);
+ await MetaGroupWrapperActions.memberSetNameTruncated(groupPk, memberPubkey, displayName);
}
if (profileKeyHex && avatarUrl) {
await MetaGroupWrapperActions.memberSetProfilePicture(groupPk, memberPubkey, {
diff --git a/ts/state/ducks/metaGroups.ts b/ts/state/ducks/metaGroups.ts
index aaee96d39..bb3259c6e 100644
--- a/ts/state/ducks/metaGroups.ts
+++ b/ts/state/ducks/metaGroups.ts
@@ -156,6 +156,8 @@ const initNewGroupInWrapper = createAsyncThunk(
const profileKeyHex = convoMember?.getProfileKey() || null;
const avatarUrl = convoMember?.getAvatarPointer() || null;
+ // we just create the members in the state. Their invite state defaults to NOT_SENT,
+ // which will make our logic kick in to send them an invite in the `GroupInviteJob`
await LibSessionUtil.createMemberAndSetDetails({
avatarUrl,
displayName,
@@ -165,9 +167,8 @@ const initNewGroupInWrapper = createAsyncThunk(
});
if (member === us) {
- await MetaGroupWrapperActions.memberSetAdmin(groupPk, member);
- } else {
- await MetaGroupWrapperActions.memberSetInvited(groupPk, member, false);
+ // we need to excplicitely mark us as having accepted the promotion
+ await MetaGroupWrapperActions.memberSetPromotionAccepted(groupPk, member);
}
}
@@ -1122,48 +1123,6 @@ const handleMemberLeftMessage = createAsyncThunk(
}
);
-const markUsAsAdmin = createAsyncThunk(
- 'group/markUsAsAdmin',
- async (
- {
- groupPk,
- secret,
- }: {
- groupPk: GroupPubkeyType;
- secret: Uint8ArrayLen64;
- },
- payloadCreator
- ): Promise => {
- const state = payloadCreator.getState() as StateType;
- if (!state.groups.infos[groupPk] || !state.groups.members[groupPk]) {
- throw new PreConditionFailed('markUsAsAdmin group not present in redux slice');
- }
- if (secret.length !== 64) {
- throw new PreConditionFailed('markUsAsAdmin secret needs to be 64');
- }
- await MetaGroupWrapperActions.loadAdminKeys(groupPk, secret);
- const us = UserUtils.getOurPubKeyStrFromCache();
-
- if (state.groups.members[groupPk].find(m => m.pubkeyHex === us)?.admin) {
- // we are already an admin, nothing to do
- return {
- groupPk,
- infos: await MetaGroupWrapperActions.infoGet(groupPk),
- members: await MetaGroupWrapperActions.memberGetAll(groupPk),
- };
- }
- await MetaGroupWrapperActions.memberSetAdmin(groupPk, us);
-
- await GroupSync.queueNewJobIfNeeded(groupPk);
-
- return {
- groupPk,
- infos: await MetaGroupWrapperActions.infoGet(groupPk),
- members: await MetaGroupWrapperActions.memberGetAll(groupPk),
- };
- }
-);
-
const inviteResponseReceived = createAsyncThunk(
'group/inviteResponseReceived',
async (
@@ -1443,21 +1402,6 @@ const metaGroupSlice = createSlice({
window.log.error('a handleMemberLeftMessage was rejected', action.error);
});
- /** markUsAsAdmin */
- builder.addCase(markUsAsAdmin.fulfilled, (state, action) => {
- const { infos, members, groupPk } = action.payload;
- state.infos[groupPk] = infos;
- state.members[groupPk] = members;
- refreshConvosModelProps([groupPk]);
- if (window.sessionFeatureFlags.debug.debugLibsessionDumps) {
- window.log.info(`groupInfo after markUsAsAdmin: ${stringify(infos)}`);
- window.log.info(`groupMembers after markUsAsAdmin: ${stringify(members)}`);
- }
- });
- builder.addCase(markUsAsAdmin.rejected, (_state, action) => {
- window.log.error('a markUsAsAdmin was rejected', action.error);
- });
-
builder.addCase(inviteResponseReceived.fulfilled, (state, action) => {
const { infos, members, groupPk } = action.payload;
state.infos[groupPk] = infos;
@@ -1488,7 +1432,6 @@ export const groupInfoActions = {
refreshGroupDetailsFromWrapper,
handleUserGroupUpdate,
currentDeviceGroupMembersChange,
- markUsAsAdmin,
inviteResponseReceived,
handleMemberLeftMessage,
currentDeviceGroupNameChange,
diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts
index 7f70a6fd6..a747f12e9 100644
--- a/ts/state/selectors/conversations.ts
+++ b/ts/state/selectors/conversations.ts
@@ -1,4 +1,5 @@
/* eslint-disable no-restricted-syntax */
+
import { createSelector } from '@reduxjs/toolkit';
import { filter, isEmpty, isFinite, isNumber, pick, sortBy, toNumber } from 'lodash';
diff --git a/ts/state/selectors/userGroups.ts b/ts/state/selectors/userGroups.ts
index 42c3b59a7..1c2b33704 100644
--- a/ts/state/selectors/userGroups.ts
+++ b/ts/state/selectors/userGroups.ts
@@ -1,4 +1,5 @@
import { useSelector } from 'react-redux';
+import { isEmpty } from 'lodash';
import { PubKey } from '../../session/types';
import { UserGroupState } from '../ducks/userGroups';
import { StateType } from '../reducer';
@@ -11,6 +12,12 @@ const getGroupById = (state: StateType, convoId?: string) => {
: undefined;
};
+export function useLibGroupWeHaveSecretKey(convoId?: string) {
+ return useSelector((state: StateType) => {
+ return !isEmpty(getGroupById(state, convoId)?.secretKey);
+ });
+}
+
export function useLibGroupInvitePending(convoId?: string) {
return useSelector((state: StateType) => getGroupById(state, convoId)?.invitePending);
}
diff --git a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_metagroup_test.ts b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_metagroup_test.ts
index 52bab12b2..6e756ee9c 100644
--- a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_metagroup_test.ts
+++ b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_metagroup_test.ts
@@ -28,8 +28,11 @@ function emptyMember(pubkeyHex: PubkeyType): GroupMemberGet {
promoted: false,
promotionFailed: false,
promotionPending: false,
- admin: false,
- removedStatus: 0,
+ inviteAccepted: false,
+ inviteNotSent: false,
+ isRemoved: false,
+ promotionNotSent: false,
+ shouldRemoveMessages: false,
pubkeyHex,
};
}
@@ -166,7 +169,7 @@ describe('libsession_metagroup', () => {
});
it('can add member by setting its promoted state, both ok and nok', () => {
- metaGroupWrapper.memberSetPromoted(member, false);
+ metaGroupWrapper.memberSetPromotionSent(member);
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
expect(metaGroupWrapper.memberGetAll()[0]).to.be.deep.eq({
...emptyMember(member),
@@ -176,7 +179,7 @@ describe('libsession_metagroup', () => {
admin: false,
});
- metaGroupWrapper.memberSetPromoted(member2, true);
+ metaGroupWrapper.memberSetPromotionFailed(member2);
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(2);
// the list is sorted by member pk, which means that index based test do not work
expect(metaGroupWrapper.memberGet(member2)).to.be.deep.eq({
@@ -224,7 +227,7 @@ describe('libsession_metagroup', () => {
it('can erase member', () => {
metaGroupWrapper.memberSetAccepted(member);
- metaGroupWrapper.memberSetPromoted(member2, false);
+ metaGroupWrapper.memberSetPromoted(member2);
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(2);
expect(metaGroupWrapper.memberGet(member)).to.be.deep.eq({
@@ -245,7 +248,7 @@ describe('libsession_metagroup', () => {
});
it('can add via name set', () => {
- metaGroupWrapper.memberSetName(member, 'member name');
+ metaGroupWrapper.memberSetNameTruncated(member, 'member name');
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
expect(metaGroupWrapper.memberGetAll()[0]).to.be.deep.eq({
...emptyMember(member),
@@ -263,14 +266,14 @@ describe('libsession_metagroup', () => {
});
it('can add via admin set', () => {
- metaGroupWrapper.memberSetAdmin(member);
+ metaGroupWrapper.memberSetPromotionAccepted(member);
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
const expected: GroupMemberGet = {
...emptyMember(member),
- admin: true,
promoted: true,
promotionFailed: false,
promotionPending: false,
+ promotionNotSent: false,
};
expect(metaGroupWrapper.memberGetAll()[0]).to.be.deep.eq(expected);
@@ -281,7 +284,8 @@ describe('libsession_metagroup', () => {
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
const expected: GroupMemberGet = {
...emptyMember(member),
- removedStatus: 2,
+ shouldRemoveMessages: true,
+ isRemoved: true,
};
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
expect(metaGroupWrapper.memberGetAll()[0]).to.be.deep.eq(expected);
@@ -292,7 +296,8 @@ describe('libsession_metagroup', () => {
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
const expected: GroupMemberGet = {
...emptyMember(member),
- removedStatus: 1,
+ shouldRemoveMessages: false,
+ isRemoved: true,
};
expect(metaGroupWrapper.memberGetAll().length).to.be.deep.eq(1);
expect(metaGroupWrapper.memberGetAll()[0]).to.be.deep.eq(expected);
@@ -319,8 +324,8 @@ describe('libsession_metagroup', () => {
});
// mark current user as admin
- metaGroupWrapper.memberSetPromoted(us.x25519KeyPair.pubkeyHex, false);
- metaGroupWrapper2.memberSetPromoted(us.x25519KeyPair.pubkeyHex, false);
+ metaGroupWrapper.memberSetPromotionAccepted(us.x25519KeyPair.pubkeyHex);
+ metaGroupWrapper2.memberSetPromotionAccepted(us.x25519KeyPair.pubkeyHex);
// add 2 normal members to each of those wrappers
const m1 = TestUtils.generateFakePubKeyStr();
diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts
index b27eea432..653dfe0c0 100644
--- a/ts/types/LocalizerKeys.ts
+++ b/ts/types/LocalizerKeys.ts
@@ -582,6 +582,7 @@ export type LocalizerKeys =
| 'userBanFailed'
| 'userBanned'
| 'userInvitedYouToGroup'
+ | 'userInvitedYouToGroupAsAdmin'
| 'userRemovedFromModerators'
| 'userUnbanFailed'
| 'userUnbanned'
@@ -609,6 +610,7 @@ export type LocalizerKeys =
| 'youLeftTheGroup'
| 'youSetYourDisappearingMessages'
| 'youWereInvitedToGroup'
+ | 'youWereInvitedToGroupAsAdmin'
| 'youWereRemovedFrom'
| 'yourSessionID'
| 'yourUniqueSessionID'
diff --git a/ts/webworker/workers/browser/libsession_worker_interface.ts b/ts/webworker/workers/browser/libsession_worker_interface.ts
index 886c27835..0cfe2e536 100644
--- a/ts/webworker/workers/browser/libsession_worker_interface.ts
+++ b/ts/webworker/workers/browser/libsession_worker_interface.ts
@@ -548,17 +548,29 @@ export const MetaGroupWrapperActions: MetaGroupWrapperActionsCalls = {
callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'memberSetAccepted', pubkeyHex]) as Promise<
ReturnType
>,
- memberSetPromoted: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType, failed: boolean) =>
+ memberSetPromoted: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType) =>
+ callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'memberSetPromoted', pubkeyHex]) as Promise<
+ ReturnType
+ >,
+ memberSetPromotionAccepted: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType) =>
callLibSessionWorker([
`MetaGroupConfig-${groupPk}`,
- 'memberSetPromoted',
+ 'memberSetPromotionAccepted',
pubkeyHex,
- failed,
- ]) as Promise>,
- memberSetAdmin: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType) =>
- callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'memberSetAdmin', pubkeyHex]) as Promise<
- ReturnType
- >,
+ ]) as Promise>,
+ memberSetPromotionFailed: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType) =>
+ callLibSessionWorker([
+ `MetaGroupConfig-${groupPk}`,
+ 'memberSetPromotionFailed',
+ pubkeyHex,
+ ]) as Promise>,
+ memberSetPromotionSent: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType) =>
+ callLibSessionWorker([
+ `MetaGroupConfig-${groupPk}`,
+ 'memberSetPromotionSent',
+ pubkeyHex,
+ ]) as Promise>,
+
memberSetInvited: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType, failed: boolean) =>
callLibSessionWorker([
`MetaGroupConfig-${groupPk}`,
@@ -566,13 +578,13 @@ export const MetaGroupWrapperActions: MetaGroupWrapperActionsCalls = {
pubkeyHex,
failed,
]) as Promise>,
- memberSetName: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType, name: string) =>
+ memberSetNameTruncated: async (groupPk: GroupPubkeyType, pubkeyHex: PubkeyType, name: string) =>
callLibSessionWorker([
`MetaGroupConfig-${groupPk}`,
- 'memberSetName',
+ 'memberSetNameTruncated',
pubkeyHex,
name,
- ]) as Promise>,
+ ]) as Promise>,
memberSetProfilePicture: async (
groupPk: GroupPubkeyType,
pubkeyHex: PubkeyType,
@@ -617,6 +629,10 @@ export const MetaGroupWrapperActions: MetaGroupWrapperActionsCalls = {
data,
timestampMs,
]) as Promise>,
+ keysAdmin: async (groupPk: GroupPubkeyType) =>
+ callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'keysAdmin']) as Promise<
+ ReturnType
+ >,
keyGetCurrentGen: async (groupPk: GroupPubkeyType) =>
callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'keyGetCurrentGen']) as Promise<
ReturnType
diff --git a/ts/window.d.ts b/ts/window.d.ts
index 9a7dc27d9..7dd755810 100644
--- a/ts/window.d.ts
+++ b/ts/window.d.ts
@@ -31,6 +31,7 @@ declare global {
useTestNet: boolean;
useClosedGroupV2: boolean;
useClosedGroupV2QAButtons: boolean;
+ useGroupV2InviteAsAdmin: boolean;
debug: {
debugLogging: boolean;
debugLibsessionDumps: boolean;
diff --git a/yarn.lock b/yarn.lock
index 518c90def..28a346a7c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2906,7 +2906,7 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
-axios@^1.3.2:
+axios@^1.6.5:
version "1.7.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621"
integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==
@@ -3527,24 +3527,24 @@ clsx@^1.0.4, clsx@^1.1.1, clsx@^1.2.1:
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
-cmake-js@7.2.1:
- version "7.2.1"
- resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-7.2.1.tgz#757c0d39994121b084bab96290baf115ee7712cd"
- integrity sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA==
+cmake-js@^7.2.1:
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-7.3.0.tgz#6fd6234b7aeec4545c1c806f9e3f7ffacd9798b2"
+ integrity sha512-dXs2zq9WxrV87bpJ+WbnGKv8WUBXDw8blNiwNHoRe/it+ptscxhQHKB1SJXa1w+kocLMeP28Tk4/eTCezg4o+w==
dependencies:
- axios "^1.3.2"
+ axios "^1.6.5"
debug "^4"
- fs-extra "^10.1.0"
+ fs-extra "^11.2.0"
lodash.isplainobject "^4.0.6"
memory-stream "^1.0.0"
- node-api-headers "^0.0.2"
+ node-api-headers "^1.1.0"
npmlog "^6.0.2"
rc "^1.2.7"
- semver "^7.3.8"
- tar "^6.1.11"
+ semver "^7.5.4"
+ tar "^6.2.0"
url-join "^4.0.1"
which "^2.0.2"
- yargs "^17.6.0"
+ yargs "^17.7.2"
color-convert@^1.9.0:
version "1.9.3"
@@ -3966,7 +3966,14 @@ debug@2.6.9, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9:
dependencies:
ms "2.0.0"
-debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+ version "4.3.5"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
+ integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
+ dependencies:
+ ms "2.1.2"
+
+debug@4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -3981,9 +3988,9 @@ debug@^3.2.7:
ms "^2.1.1"
debug@^4:
- version "4.3.5"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
- integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
+ version "4.3.6"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
+ integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
dependencies:
ms "2.1.2"
@@ -5177,7 +5184,7 @@ fs-extra@^10.0.0, fs-extra@^10.1.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
-fs-extra@^11.0.0:
+fs-extra@^11.0.0, fs-extra@^11.2.0:
version "11.2.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
@@ -6599,9 +6606,12 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
-"libsession_util_nodejs@link:../libsession-util-nodejs":
- version "0.0.0"
- uid ""
+"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.21/libsession_util_nodejs-v0.3.21.tar.gz":
+ version "0.3.21"
+ resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.21/libsession_util_nodejs-v0.3.21.tar.gz#64705b1f7c934ca32f929ea8127370cc82bab97a"
+ dependencies:
+ cmake-js "^7.2.1"
+ node-addon-api "^6.1.0"
libsodium-sumo@^0.7.13:
version "0.7.13"
@@ -7483,10 +7493,10 @@ node-addon-api@^6.1.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76"
integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==
-node-api-headers@^0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/node-api-headers/-/node-api-headers-0.0.2.tgz#31f4c6c2750b63e598128e76a60aefca6d76ac5d"
- integrity sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==
+node-api-headers@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/node-api-headers/-/node-api-headers-1.2.0.tgz#b717cd420aec79031f8dc83a50eb0a8bdf24c70d"
+ integrity sha512-L9AiEkBfgupC0D/LsudLPOhzy/EdObsp+FHyL1zSK0kKv5FDA9rJMoRz8xd+ojxzlqfg0tTZm2h8ot2nS7bgRA==
node-dir@^0.1.17:
version "0.1.17"
@@ -9112,7 +9122,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.4:
+semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.4:
version "7.6.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
@@ -9707,7 +9717,7 @@ tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
-tar@^6.1.0, tar@^6.1.11:
+tar@^6.1.0, tar@^6.1.11, tar@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
@@ -10695,7 +10705,7 @@ yargs@^15.1.0:
y18n "^4.0.0"
yargs-parser "^18.1.2"
-yargs@^17.0.0, yargs@^17.0.1, yargs@^17.6.0, yargs@^17.6.2:
+yargs@^17.0.0, yargs@^17.0.1, yargs@^17.6.2, yargs@^17.7.2:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==