feat: add meta merge and update of name+members

pull/2873/head
Audric Ackermann 2 years ago
parent be50aa7a2e
commit 90f4dd761c

@ -24,7 +24,7 @@ import { SessionUtilConvoInfoVolatile } from '../session/utils/libsession/libses
import { SessionUtilUserGroups } from '../session/utils/libsession/libsession_utils_user_groups'; import { SessionUtilUserGroups } from '../session/utils/libsession/libsession_utils_user_groups';
import { configurationMessageReceived, trigger } from '../shims/events'; import { configurationMessageReceived, trigger } from '../shims/events';
import { getCurrentlySelectedConversationOutsideRedux } from '../state/selectors/conversations'; import { getCurrentlySelectedConversationOutsideRedux } from '../state/selectors/conversations';
import { assertUnreachable } from '../types/sqlSharedTypes'; import { assertUnreachable, stringify, toFixedUint8ArrayOfLength } from '../types/sqlSharedTypes';
import { BlockedNumberController } from '../util'; import { BlockedNumberController } from '../util';
import { Storage, setLastProfileUpdateTimestamp } from '../util/storage'; import { Storage, setLastProfileUpdateTimestamp } from '../util/storage';
// eslint-disable-next-line import/no-unresolved, import/extensions // eslint-disable-next-line import/no-unresolved, import/extensions
@ -46,6 +46,7 @@ import {
import { addKeyPairToCacheAndDBIfNeeded } from './closedGroups'; import { addKeyPairToCacheAndDBIfNeeded } from './closedGroups';
import { HexKeyPair } from './keypairs'; import { HexKeyPair } from './keypairs';
import { queueAllCachedFromSource } from './receiver'; import { queueAllCachedFromSource } from './receiver';
import { HexString } from '../node/hexStrings';
type IncomingUserResult = { type IncomingUserResult = {
needsPush: boolean; needsPush: boolean;
@ -609,6 +610,42 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
} }
} }
async function handleGroupUpdate(latestEnvelopeTimestamp: number) {
// first let's check which groups needs to be joined or left by doing a diff of what is in the wrapper and what is in the DB
const allGoupsInWrapper = await UserGroupsWrapperActions.getAllGroups();
const allGoupsIdsInWrapper = allGoupsInWrapper.map(m => m.pubkeyHex);
console.warn('allGoupsIdsInWrapper', stringify(allGoupsIdsInWrapper));
const userEdKeypair = await UserUtils.getUserED25519KeyPairBytes();
if (!userEdKeypair) {
throw new Error('userEdKeypair is not set');
}
for (let index = 0; index < allGoupsInWrapper.length; index++) {
const groupInWrapper = allGoupsInWrapper[index];
if (!getConversationController().get(groupInWrapper.pubkeyHex)) {
// dump is always empty when creating a new groupInfo
await MetaGroupWrapperActions.init(groupInWrapper.pubkeyHex, {
metaDumped: null,
userEd25519Secretkey: toFixedUint8ArrayOfLength(userEdKeypair.privKeyBytes, 64),
groupEd25519Secretkey: groupInWrapper.secretKey,
groupEd25519Pubkey: toFixedUint8ArrayOfLength(
HexString.fromHexString(groupInWrapper.pubkeyHex.slice(2)),
32
),
});
const created = await getConversationController().getOrCreateAndWait(
groupInWrapper.pubkeyHex,
ConversationTypeEnum.GROUPV3
);
created.set({ active_at: latestEnvelopeTimestamp });
await created.commit();
getSwarmPollingInstance().addGroupId(PubKey.cast(groupInWrapper.pubkeyHex));
}
}
}
async function handleUserGroupsUpdate(result: IncomingUserResult) { async function handleUserGroupsUpdate(result: IncomingUserResult) {
const toHandle = SessionUtilUserGroups.getUserGroupTypes(); const toHandle = SessionUtilUserGroups.getUserGroupTypes();
for (let index = 0; index < toHandle.length; index++) { for (let index = 0; index < toHandle.length; index++) {
@ -621,6 +658,9 @@ async function handleUserGroupsUpdate(result: IncomingUserResult) {
case 'LegacyGroup': case 'LegacyGroup':
await handleLegacyGroupUpdate(result.latestEnvelopeTimestamp); await handleLegacyGroupUpdate(result.latestEnvelopeTimestamp);
break; break;
case 'Group':
await handleGroupUpdate(result.latestEnvelopeTimestamp);
break;
default: default:
assertUnreachable(typeToHandle, `handleUserGroupsUpdate unhandled type "${typeToHandle}"`); assertUnreachable(typeToHandle, `handleUserGroupsUpdate unhandled type "${typeToHandle}"`);
@ -731,6 +771,10 @@ async function handleConvoInfoVolatileUpdate() {
} }
break; break;
case 'Group':
// debugger; // we need to update the current read messages of that group 03 with what we have in the wrapper // debugger
break;
default: default:
assertUnreachable(type, `handleConvoInfoVolatileUpdate: unhandeld switch case: ${type}`); assertUnreachable(type, `handleConvoInfoVolatileUpdate: unhandeld switch case: ${type}`);
} }

@ -4,6 +4,7 @@ import { SnodeNamespaces, SnodeNamespacesGroup } from './namespaces';
export type SwarmForSubRequest = { method: 'get_swarm'; params: { pubkey: string } }; export type SwarmForSubRequest = { method: 'get_swarm'; params: { pubkey: string } };
type WithRetrieveMethod = { method: 'retrieve' };
type WithMaxCountSize = { max_count?: number; max_size?: number }; type WithMaxCountSize = { max_count?: number; max_size?: number };
type WithPubkeyAsString = { pubkey: string }; type WithPubkeyAsString = { pubkey: string };
type WithPubkeyAsGroupPubkey = { pubkey: GroupPubkeyType }; type WithPubkeyAsGroupPubkey = { pubkey: GroupPubkeyType };
@ -14,8 +15,7 @@ type RetrieveAlwaysNeeded = {
timestamp?: number; timestamp?: number;
}; };
export type RetrievePubkeySubRequestType = { export type RetrievePubkeySubRequestType = WithRetrieveMethod & {
method: 'retrieve';
params: { params: {
signature: string; signature: string;
pubkey_ed25519: string; pubkey_ed25519: string;
@ -33,8 +33,7 @@ export type RetrievePubkeySubRequestType = {
*/ */
// type UnauthenticatedStoreNamespaces = -30 | -20 | -10 | 0 | 10 | 20 | 30; // type UnauthenticatedStoreNamespaces = -30 | -20 | -10 | 0 | 10 | 20 | 30;
export type RetrieveLegacyClosedGroupSubRequestType = { export type RetrieveLegacyClosedGroupSubRequestType = WithRetrieveMethod & {
method: 'retrieve';
params: { params: {
namespace: SnodeNamespaces.LegacyClosedGroup; // legacy closed groups retrieve are not authenticated because the clients do not have a shared key namespace: SnodeNamespaces.LegacyClosedGroup; // legacy closed groups retrieve are not authenticated because the clients do not have a shared key
} & RetrieveAlwaysNeeded & } & RetrieveAlwaysNeeded &
@ -42,8 +41,7 @@ export type RetrieveLegacyClosedGroupSubRequestType = {
WithPubkeyAsString; WithPubkeyAsString;
}; };
export type RetrieveGroupAdminSubRequestType = { export type RetrieveGroupAdminSubRequestType = WithRetrieveMethod & {
method: 'retrieve';
params: { params: {
signature: string; signature: string;
namespace: SnodeNamespacesGroup; namespace: SnodeNamespacesGroup;

@ -23,8 +23,8 @@ export async function doSnodeBatchRequest(
): Promise<NotEmptyArrayOfBatchResults> { ): Promise<NotEmptyArrayOfBatchResults> {
console.warn( console.warn(
`doSnodeBatchRequest "${method}":`, `doSnodeBatchRequest "${method}":`,
JSON.stringify(subRequests.map(m => m.method)), JSON.stringify(subRequests.map(m => m.method))
subRequests // subRequests
); );
const result = await snodeRpc({ const result = await snodeRpc({
method, method,

@ -102,7 +102,7 @@ async function retrieveRequestForGroup({
const group = await UserGroupsWrapperActions.getGroup(groupPk); const group = await UserGroupsWrapperActions.getGroup(groupPk);
const groupSecretKey = group?.secretKey; const groupSecretKey = group?.secretKey;
if (isNil(groupSecretKey) || isEmpty(groupSecretKey)) { if (isNil(groupSecretKey) || isEmpty(groupSecretKey)) {
throw new Error(`sendMessagesDataToSnode: failed to find group admin secret key in wrapper`); throw new Error(`retrieveRequestForGroup: failed to find group admin secret key in wrapper`);
} }
const signatureBuilt = await SnodeSignature.getSnodeGroupSignatureParams({ const signatureBuilt = await SnodeSignature.getSnodeGroupSignatureParams({
...retrieveParam, ...retrieveParam,

@ -26,7 +26,7 @@ import { SnodeNamespace, SnodeNamespaces } from './namespaces';
import { SnodeAPIRetrieve } from './retrieveRequest'; import { SnodeAPIRetrieve } from './retrieveRequest';
import { SwarmPollingGroupConfig } from './swarm_polling_config/SwarmPollingGroupConfig'; import { SwarmPollingGroupConfig } from './swarm_polling_config/SwarmPollingGroupConfig';
import { SwarmPollingUserConfig } from './swarm_polling_config/SwarmPollingUserConfig'; import { SwarmPollingUserConfig } from './swarm_polling_config/SwarmPollingUserConfig';
import { RetrieveMessageItem, RetrieveMessagesResultsBatched } from './types'; import { RetrieveMessageItem, RetrieveMessageItemWithNamespace, RetrieveMessagesResultsBatched, RetrieveRequestResult } from './types';
import { GroupPubkeyType } from 'libsession_util_nodejs'; import { GroupPubkeyType } from 'libsession_util_nodejs';
import { assertUnreachable } from '../../../types/sqlSharedTypes'; import { assertUnreachable } from '../../../types/sqlSharedTypes';
@ -547,27 +547,28 @@ export class SwarmPolling {
} }
} }
function retrieveItemWithNamespace(results: RetrieveRequestResult[] ) {
return flatten(
compact(results.map(result => result.messages.messages?.map(r => ({ ...r, namespace: result.namespace })))));
}
function filterMessagesPerTypeOfConvo<T extends ConversationTypeEnum>( function filterMessagesPerTypeOfConvo<T extends ConversationTypeEnum>(
type: T, type: T,
retrieveResults: RetrieveMessagesResultsBatched retrieveResults: RetrieveMessagesResultsBatched
): { confMessages: Array<RetrieveMessageItem> | null; otherMessages: Array<RetrieveMessageItem> } { ): { confMessages: Array<RetrieveMessageItemWithNamespace> | null; otherMessages: Array<RetrieveMessageItemWithNamespace> } {
switch (type) { switch (type) {
case ConversationTypeEnum.PRIVATE: { case ConversationTypeEnum.PRIVATE: {
const confMessages = flatten( const userConfs = retrieveResults
compact( .filter(m => SnodeNamespace.isUserConfigNamespace(m.namespace));
retrieveResults const userOthers = retrieveResults
.filter(m => SnodeNamespace.isUserConfigNamespace(m.namespace)) .filter(m => !SnodeNamespace.isUserConfigNamespace(m.namespace));
.map(r => r.messages.messages)
) const confMessages =
); retrieveItemWithNamespace(userConfs)
const otherMessages =
retrieveItemWithNamespace(userOthers)
const otherMessages = flatten(
compact(
retrieveResults
.filter(m => !SnodeNamespace.isUserConfigNamespace(m.namespace))
.map(r => r.messages.messages)
)
);
return { confMessages, otherMessages: uniqBy(otherMessages, x => x.hash) }; return { confMessages, otherMessages: uniqBy(otherMessages, x => x.hash) };
} }
@ -575,27 +576,25 @@ function filterMessagesPerTypeOfConvo<T extends ConversationTypeEnum>(
case ConversationTypeEnum.GROUP: case ConversationTypeEnum.GROUP:
return { return {
confMessages: null, confMessages: null,
otherMessages: flatten(compact(retrieveResults.map(m => m.messages.messages))), otherMessages: retrieveItemWithNamespace(retrieveResults),
}; };
case ConversationTypeEnum.GROUPV3: { case ConversationTypeEnum.GROUPV3: {
const confMessages = flatten(
compact(
retrieveResults
.filter(m => SnodeNamespace.isGroupConfigNamespace(m.namespace))
.map(r => r.messages.messages)
)
);
const otherMessages = flatten( const groupConfs = retrieveResults
compact( .filter(m => SnodeNamespace.isGroupConfigNamespace(m.namespace));
retrieveResults const groupOthers = retrieveResults
.filter(m => !SnodeNamespace.isGroupConfigNamespace(m.namespace)) .filter(m => !SnodeNamespace.isGroupConfigNamespace(m.namespace));
.map(r => r.messages.messages)
) const groupConfMessages =
); retrieveItemWithNamespace(groupConfs)
const groupOtherMessages =
retrieveItemWithNamespace(groupOthers)
return { confMessages: groupConfMessages, otherMessages: uniqBy(groupOtherMessages, x => x.hash) };
return { confMessages, otherMessages: uniqBy(otherMessages, x => x.hash) };
} }
default: default:

@ -1,11 +1,13 @@
import { GroupPubkeyType } from 'libsession_util_nodejs'; import { GroupPubkeyType } from 'libsession_util_nodejs';
import { EnvelopePlus } from '../../../../receiver/types'; import { stringify } from '../../../../types/sqlSharedTypes';
import { MetaGroupWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import { ed25519Str } from '../../../onions/onionPath'; import { ed25519Str } from '../../../onions/onionPath';
import { RetrieveMessageItem } from '../types'; import { fromBase64ToArray } from '../../../utils/String';
import { SwarmPollingConfigShared } from './SwarmPollingConfigShared'; import { SnodeNamespaces } from '../namespaces';
import { RetrieveMessageItemWithNamespace } from '../types';
async function handleGroupSharedConfigMessages( async function handleGroupSharedConfigMessages(
groupConfigMessagesMerged: Array<RetrieveMessageItem>, groupConfigMessagesMerged: Array<RetrieveMessageItemWithNamespace>,
groupPk: GroupPubkeyType groupPk: GroupPubkeyType
) { ) {
window.log.info( window.log.info(
@ -14,34 +16,59 @@ async function handleGroupSharedConfigMessages(
} for groupPk:${ed25519Str(groupPk)}` } for groupPk:${ed25519Str(groupPk)}`
); );
try { try {
const extractedConfigMessage = SwarmPollingConfigShared.extractWebSocketContents( const infos = groupConfigMessagesMerged
groupConfigMessagesMerged .filter(m => m.namespace === SnodeNamespaces.ClosedGroupInfo)
.map(info => {
return { data: fromBase64ToArray(info.data), hash: info.hash };
});
const members = groupConfigMessagesMerged
.filter(m => m.namespace === SnodeNamespaces.ClosedGroupMembers)
.map(info => {
return { data: fromBase64ToArray(info.data), hash: info.hash };
});
const keys = groupConfigMessagesMerged
.filter(m => m.namespace === SnodeNamespaces.ClosedGroupKeys)
.map(info => {
return {
data: fromBase64ToArray(info.data),
hash: info.hash,
timestampMs: info.timestamp,
};
});
const toMerge = {
groupInfo: infos,
groupKeys: keys,
groupMember: members,
};
console.info(`About to merge ${stringify(toMerge)}`);
console.info(`dumps before ${stringify(await MetaGroupWrapperActions.metaDump(groupPk))}`);
console.info(
`groupInfo before merge: ${stringify(await MetaGroupWrapperActions.infoGet(groupPk))}`
); );
const countMerged = await MetaGroupWrapperActions.metaMerge(groupPk, toMerge);
const allDecryptedConfigMessages = await SwarmPollingConfigShared.decryptSharedConfigMessages( console.info(
extractedConfigMessage, `countMerged ${countMerged}, groupInfo after merge: ${stringify(
async (_envelope: EnvelopePlus) => { await MetaGroupWrapperActions.infoGet(groupPk)
console.warn('decrypt closed group incoming shared message to do'); )}`
return null;
}
); );
console.info(`dumps after ${stringify(await MetaGroupWrapperActions.metaDump(groupPk))}`);
if (allDecryptedConfigMessages.length) { // if (allDecryptedConfigMessages.length) {
try { // try {
window.log.info( // window.log.info(
`handleGroupSharedConfigMessages of "${allDecryptedConfigMessages.length}" messages with libsession` // `handleGroupSharedConfigMessages of "${allDecryptedConfigMessages.length}" messages with libsession`
); // );
console.warn('HANDLING OF INCOMING GROUP TODO '); // console.warn('HANDLING OF INCOMING GROUP TODO ');
// await ConfigMessageHandler.handleUserConfigMessagesViaLibSession( // // await ConfigMessageHandler.handleUserConfigMessagesViaLibSession(
// allDecryptedConfigMessages // // allDecryptedConfigMessages
// ); // // );
} catch (e) { // } catch (e) {
const allMessageHases = allDecryptedConfigMessages.map(m => m.messageHash).join(','); // const allMessageHases = allDecryptedConfigMessages.map(m => m.messageHash).join(',');
window.log.warn( // window.log.warn(
`failed to handle group messages hashes "${allMessageHases}" with libsession. Error: "${e.message}"` // `failed to handle group messages hashes "${allMessageHases}" with libsession. Error: "${e.message}"`
); // );
} // }
} // }
} catch (e) { } catch (e) {
window.log.warn( window.log.warn(
`handleGroupSharedConfigMessages of ${groupConfigMessagesMerged.length} failed with ${e.message}` `handleGroupSharedConfigMessages of ${groupConfigMessagesMerged.length} failed with ${e.message}`

@ -7,6 +7,10 @@ export type RetrieveMessageItem = {
timestamp: number; timestamp: number;
}; };
export type RetrieveMessageItemWithNamespace = RetrieveMessageItem & {
namespace: SnodeNamespaces; // the namespace from which this message was fetched
};
export type RetrieveMessagesResultsContent = { export type RetrieveMessagesResultsContent = {
hf?: Array<number>; hf?: Array<number>;
messages?: Array<RetrieveMessageItem>; messages?: Array<RetrieveMessageItem>;

@ -29,6 +29,7 @@ import {
PersistedJob, PersistedJob,
RunJobResult, RunJobResult,
} from '../PersistedJob'; } from '../PersistedJob';
import { stringify } from '../../../../types/sqlSharedTypes';
const defaultMsBetweenRetries = 15000; // a long time between retries, to avoid running multiple jobs at the same time, when one was postponed at the same time as one already planned (5s) const defaultMsBetweenRetries = 15000; // a long time between retries, to avoid running multiple jobs at the same time, when one was postponed at the same time as one already planned (5s)
const defaultMaxAttempts = 2; const defaultMaxAttempts = 2;
@ -210,12 +211,14 @@ class GroupSyncJob extends PersistedJob<GroupSyncPersistedData> {
data: item.data, data: item.data,
}; };
}); });
console.warn(`msgs ${stringify(msgs)}`);
const result = await MessageSender.sendEncryptedDataToSnode( const result = await MessageSender.sendEncryptedDataToSnode(
msgs, msgs,
thisJobDestination, thisJobDestination,
oldHashesToDelete oldHashesToDelete
); );
console.warn(`result ${stringify(result)}`);
const expectedReplyLength = const expectedReplyLength =
singleDestChanges.messages.length + (oldHashesToDelete.size ? 1 : 0); singleDestChanges.messages.length + (oldHashesToDelete.size ? 1 : 0);

@ -118,6 +118,9 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise<
); );
} }
break; break;
case 'Group':
// we need to keep track of the convo volatile info for the new group now. // TODO AUDRIC debugger
break;
case 'Community': case 'Community':
try { try {
const asOpengroup = foundConvo.toOpenGroupV2(); const asOpengroup = foundConvo.toOpenGroupV2();

@ -10,6 +10,7 @@ import {
} from '../../../types/sqlSharedTypes'; } from '../../../types/sqlSharedTypes';
import { UserGroupsWrapperActions } from '../../../webworker/workers/browser/libsession_worker_interface'; import { UserGroupsWrapperActions } from '../../../webworker/workers/browser/libsession_worker_interface';
import { getConversationController } from '../../conversations'; import { getConversationController } from '../../conversations';
import { PubKey } from '../../types';
/** /**
* Returns true if that conversation is an active group * Returns true if that conversation is an active group
@ -33,6 +34,10 @@ function isLegacyGroupToStoreInWrapper(convo: ConversationModel): boolean {
); );
} }
function isGroupToStoreInWrapper(convo: ConversationModel): boolean {
return convo.isGroup() && PubKey.isClosedGroupV3(convo.id) && convo.isActive(); // TODO should we filter by left/kicked or they are on the wrapper itself?
}
/** /**
* We do not want to include groups left in the wrapper, but when receiving a list * We do not want to include groups left in the wrapper, but when receiving a list
* of wrappers from the network we need to check against the one present locally * of wrappers from the network we need to check against the one present locally
@ -73,6 +78,8 @@ async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise
const convoType: UserGroupsType = isCommunityToStoreInWrapper(foundConvo) const convoType: UserGroupsType = isCommunityToStoreInWrapper(foundConvo)
? 'Community' ? 'Community'
: PubKey.isClosedGroupV3(convoId)
? 'Group'
: 'LegacyGroup'; : 'LegacyGroup';
switch (convoType) { switch (convoType) {
@ -135,6 +142,9 @@ async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise
// we still let this go through // we still let this go through
} }
break; break;
case 'Group':
// debugger;
break;
default: default:
assertUnreachable( assertUnreachable(
@ -175,7 +185,7 @@ async function removeCommunityFromWrapper(_convoId: string, fullUrlWithOrWithout
* whole other bunch of issues because it is a native node module. * whole other bunch of issues because it is a native node module.
*/ */
function getUserGroupTypes(): Array<UserGroupsType> { function getUserGroupTypes(): Array<UserGroupsType> {
return ['Community', 'LegacyGroup']; return ['Community', 'LegacyGroup', 'Group'];
} }
export const SessionUtilUserGroups = { export const SessionUtilUserGroups = {
@ -193,4 +203,7 @@ export const SessionUtilUserGroups = {
// legacy group // legacy group
isLegacyGroupToStoreInWrapper, isLegacyGroupToStoreInWrapper,
isLegacyGroupToRemoveFromDBIfNotInWrapper, isLegacyGroupToRemoveFromDBIfNotInWrapper,
// group 03
isGroupToStoreInWrapper,
}; };

@ -1,17 +1,17 @@
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { GroupInfoGet, GroupInfoShared, GroupPubkeyType } from 'libsession_util_nodejs'; import { GroupInfoGet, GroupInfoShared, GroupPubkeyType } from 'libsession_util_nodejs';
import { uniq } from 'lodash';
import { ConversationTypeEnum } from '../../models/conversationAttributes'; import { ConversationTypeEnum } from '../../models/conversationAttributes';
import { HexString } from '../../node/hexStrings'; import { HexString } from '../../node/hexStrings';
import { ClosedGroup } from '../../session'; import { ClosedGroup } from '../../session';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { UserUtils } from '../../session/utils'; import { UserUtils } from '../../session/utils';
import { GroupSync } from '../../session/utils/job_runners/jobs/GroupConfigJob';
import { toFixedUint8ArrayOfLength } from '../../types/sqlSharedTypes';
import { import {
MetaGroupWrapperActions, MetaGroupWrapperActions,
UserGroupsWrapperActions, UserGroupsWrapperActions,
} from '../../webworker/workers/browser/libsession_worker_interface'; } from '../../webworker/workers/browser/libsession_worker_interface';
import { toFixedUint8ArrayOfLength } from '../../types/sqlSharedTypes';
import { uniq } from 'lodash';
import { GroupSync } from '../../session/utils/job_runners/jobs/GroupConfigJob';
type GroupInfoGetWithId = GroupInfoGet & { id: GroupPubkeyType }; type GroupInfoGetWithId = GroupInfoGet & { id: GroupPubkeyType };
@ -46,6 +46,9 @@ const initNewGroupInfoInWrapper = createAsyncThunk(
}): Promise<GroupInfoGetWithId> => { }): Promise<GroupInfoGetWithId> => {
try { try {
const newGroup = await UserGroupsWrapperActions.createGroup(); const newGroup = await UserGroupsWrapperActions.createGroup();
await UserGroupsWrapperActions.setGroup(newGroup);
const ourEd25519KeypairBytes = await UserUtils.getUserED25519KeyPairBytes(); const ourEd25519KeypairBytes = await UserUtils.getUserED25519KeyPairBytes();
if (!ourEd25519KeypairBytes) { if (!ourEd25519KeypairBytes) {
throw new Error('Current user has no priv ed25519 key?'); throw new Error('Current user has no priv ed25519 key?');
@ -61,12 +64,28 @@ const initNewGroupInfoInWrapper = createAsyncThunk(
groupEd25519Pubkey: toFixedUint8ArrayOfLength(groupEd2519Pk, 32), groupEd25519Pubkey: toFixedUint8ArrayOfLength(groupEd2519Pk, 32),
}); });
await Promise.all(
groupDetails.members.map(async member => {
const created = await MetaGroupWrapperActions.memberGetOrConstruct(
newGroup.pubkeyHex,
member
);
await MetaGroupWrapperActions.memberSetInvited(
newGroup.pubkeyHex,
created.pubkeyHex,
false
);
})
);
const infos = await MetaGroupWrapperActions.infoGet(newGroup.pubkeyHex); const infos = await MetaGroupWrapperActions.infoGet(newGroup.pubkeyHex);
if (!infos) { if (!infos) {
throw new Error( throw new Error(
`getInfos of ${newGroup.pubkeyHex} returned empty result even if it was just init.` `getInfos of ${newGroup.pubkeyHex} returned empty result even if it was just init.`
); );
} }
infos.name = groupDetails.groupName;
await MetaGroupWrapperActions.infoSet(newGroup.pubkeyHex, infos);
const convo = await getConversationController().getOrCreateAndWait( const convo = await getConversationController().getOrCreateAndWait(
newGroup.pubkeyHex, newGroup.pubkeyHex,

@ -12,7 +12,7 @@ import { isArray, isEmpty, isEqual } from 'lodash';
import { OpenGroupV2Room } from '../data/opengroups'; import { OpenGroupV2Room } from '../data/opengroups';
import { ConversationAttributes } from '../models/conversationAttributes'; import { ConversationAttributes } from '../models/conversationAttributes';
import { OpenGroupRequestCommonType } from '../session/apis/open_group_api/opengroupV2/ApiUtil'; import { OpenGroupRequestCommonType } from '../session/apis/open_group_api/opengroupV2/ApiUtil';
import { fromHexToArray } from '../session/utils/String'; import { fromHexToArray, toHex } from '../session/utils/String';
import { ConfigWrapperObjectTypesMeta } from '../webworker/workers/browser/libsession_worker_functions'; import { ConfigWrapperObjectTypesMeta } from '../webworker/workers/browser/libsession_worker_functions';
/** /**
@ -281,3 +281,9 @@ export function toFixedUint8ArrayOfLength<T extends number>(
`toFixedUint8ArrayOfLength invalid. Expected length ${length} but got: ${data.length}` `toFixedUint8ArrayOfLength invalid. Expected length ${length} but got: ${data.length}`
); );
} }
export function stringify(obj: unknown) {
return JSON.stringify(obj, (_key, value) =>
value instanceof Uint8Array ? `Uint8Array(${value.length}): ${toHex(value)}` : value
);
}

@ -14,6 +14,7 @@ import {
UserConfigWrapperActionsCalls, UserConfigWrapperActionsCalls,
UserGroupsWrapperActionsCalls, UserGroupsWrapperActionsCalls,
UserGroupsSet, UserGroupsSet,
MergeSingle,
} from 'libsession_util_nodejs'; } from 'libsession_util_nodejs';
import { join } from 'path'; import { join } from 'path';
@ -76,7 +77,7 @@ export const GenericWrapperActions: GenericWrapperActionsCalls = {
>, >,
dump: async (wrapperId: ConfigWrapperUser) => dump: async (wrapperId: ConfigWrapperUser) =>
callLibSessionWorker([wrapperId, 'dump']) as ReturnType<GenericWrapperActionsCalls['dump']>, callLibSessionWorker([wrapperId, 'dump']) as ReturnType<GenericWrapperActionsCalls['dump']>,
merge: async (wrapperId: ConfigWrapperUser, toMerge: Array<{ hash: string; data: Uint8Array }>) => merge: async (wrapperId: ConfigWrapperUser, toMerge: Array<MergeSingle>) =>
callLibSessionWorker([wrapperId, 'merge', toMerge]) as ReturnType< callLibSessionWorker([wrapperId, 'merge', toMerge]) as ReturnType<
GenericWrapperActionsCalls['merge'] GenericWrapperActionsCalls['merge']
>, >,
@ -103,8 +104,7 @@ export const UserConfigWrapperActions: UserConfigWrapperActionsCalls = {
confirmPushed: async (seqno: number, hash: string) => confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('UserConfig', seqno, hash), GenericWrapperActions.confirmPushed('UserConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('UserConfig'), dump: async () => GenericWrapperActions.dump('UserConfig'),
merge: async (toMerge: Array<{ hash: string; data: Uint8Array }>) => merge: async (toMerge: Array<MergeSingle>) => GenericWrapperActions.merge('UserConfig', toMerge),
GenericWrapperActions.merge('UserConfig', toMerge),
needsDump: async () => GenericWrapperActions.needsDump('UserConfig'), needsDump: async () => GenericWrapperActions.needsDump('UserConfig'),
needsPush: async () => GenericWrapperActions.needsPush('UserConfig'), needsPush: async () => GenericWrapperActions.needsPush('UserConfig'),
push: async () => GenericWrapperActions.push('UserConfig'), push: async () => GenericWrapperActions.push('UserConfig'),
@ -149,7 +149,7 @@ export const ContactsWrapperActions: ContactsWrapperActionsCalls = {
confirmPushed: async (seqno: number, hash: string) => confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('ContactsConfig', seqno, hash), GenericWrapperActions.confirmPushed('ContactsConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('ContactsConfig'), dump: async () => GenericWrapperActions.dump('ContactsConfig'),
merge: async (toMerge: Array<{ hash: string; data: Uint8Array }>) => merge: async (toMerge: Array<MergeSingle>) =>
GenericWrapperActions.merge('ContactsConfig', toMerge), GenericWrapperActions.merge('ContactsConfig', toMerge),
needsDump: async () => GenericWrapperActions.needsDump('ContactsConfig'), needsDump: async () => GenericWrapperActions.needsDump('ContactsConfig'),
needsPush: async () => GenericWrapperActions.needsPush('ContactsConfig'), needsPush: async () => GenericWrapperActions.needsPush('ContactsConfig'),
@ -184,7 +184,7 @@ export const UserGroupsWrapperActions: UserGroupsWrapperActionsCalls = {
confirmPushed: async (seqno: number, hash: string) => confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('UserGroupsConfig', seqno, hash), GenericWrapperActions.confirmPushed('UserGroupsConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('UserGroupsConfig'), dump: async () => GenericWrapperActions.dump('UserGroupsConfig'),
merge: async (toMerge: Array<{ hash: string; data: Uint8Array }>) => merge: async (toMerge: Array<MergeSingle>) =>
GenericWrapperActions.merge('UserGroupsConfig', toMerge), GenericWrapperActions.merge('UserGroupsConfig', toMerge),
needsDump: async () => GenericWrapperActions.needsDump('UserGroupsConfig'), needsDump: async () => GenericWrapperActions.needsDump('UserGroupsConfig'),
needsPush: async () => GenericWrapperActions.needsPush('UserGroupsConfig'), needsPush: async () => GenericWrapperActions.needsPush('UserGroupsConfig'),
@ -281,7 +281,7 @@ export const ConvoInfoVolatileWrapperActions: ConvoInfoVolatileWrapperActionsCal
confirmPushed: async (seqno: number, hash: string) => confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('ConvoInfoVolatileConfig', seqno, hash), GenericWrapperActions.confirmPushed('ConvoInfoVolatileConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('ConvoInfoVolatileConfig'), dump: async () => GenericWrapperActions.dump('ConvoInfoVolatileConfig'),
merge: async (toMerge: Array<{ hash: string; data: Uint8Array }>) => merge: async (toMerge: Array<MergeSingle>) =>
GenericWrapperActions.merge('ConvoInfoVolatileConfig', toMerge), GenericWrapperActions.merge('ConvoInfoVolatileConfig', toMerge),
needsDump: async () => GenericWrapperActions.needsDump('ConvoInfoVolatileConfig'), needsDump: async () => GenericWrapperActions.needsDump('ConvoInfoVolatileConfig'),
needsPush: async () => GenericWrapperActions.needsPush('ConvoInfoVolatileConfig'), needsPush: async () => GenericWrapperActions.needsPush('ConvoInfoVolatileConfig'),
@ -396,6 +396,13 @@ export const MetaGroupWrapperActions: MetaGroupWrapperActionsCalls = {
callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'metaConfirmPushed', args]) as Promise< callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'metaConfirmPushed', args]) as Promise<
ReturnType<MetaGroupWrapperActionsCalls['metaConfirmPushed']> ReturnType<MetaGroupWrapperActionsCalls['metaConfirmPushed']>
>, >,
metaMerge: async (
groupPk: GroupPubkeyType,
args: Parameters<MetaGroupWrapperActionsCalls['metaMerge']>[1]
) =>
callLibSessionWorker([`MetaGroupConfig-${groupPk}`, 'metaMerge', args]) as Promise<
ReturnType<MetaGroupWrapperActionsCalls['metaMerge']>
>,
/** GroupInfo wrapper specific actions */ /** GroupInfo wrapper specific actions */
infoGet: async (groupPk: GroupPubkeyType) => infoGet: async (groupPk: GroupPubkeyType) =>

Loading…
Cancel
Save