fix: add return types for libsession wrapper functions

pull/2620/head
Audric Ackermann 2 years ago
parent 90804491d6
commit 4bfcf91193

@ -3,32 +3,25 @@ import { QRCode } from 'react-qr-svg';
import { Avatar, AvatarSize } from '../avatar/Avatar'; import { Avatar, AvatarSize } from '../avatar/Avatar';
import { YourSessionIDPill, YourSessionIDSelectable } from '../basic/YourSessionIDPill';
import { SyncUtils, ToastUtils, UserUtils } from '../../session/utils'; import { SyncUtils, ToastUtils, UserUtils } from '../../session/utils';
import { YourSessionIDPill, YourSessionIDSelectable } from '../basic/YourSessionIDPill';
import { ConversationModel } from '../../models/conversation'; import { ConversationModel } from '../../models/conversation';
import { getConversationController } from '../../session/conversations';
import autoBind from 'auto-bind'; import autoBind from 'auto-bind';
import { editProfileModal } from '../../state/ducks/modalDialog';
import { uploadOurAvatar } from '../../interactions/conversationInteractions'; import { uploadOurAvatar } from '../../interactions/conversationInteractions';
import { ConversationTypeEnum } from '../../models/conversationAttributes';
import { MAX_USERNAME_BYTES } from '../../session/constants';
import { getConversationController } from '../../session/conversations';
import { ConfigurationSync } from '../../session/utils/job_runners/jobs/ConfigurationSyncJob';
import { sanitizeSessionUsername } from '../../session/utils/String';
import { editProfileModal } from '../../state/ducks/modalDialog';
import { pickFileForAvatar } from '../../types/attachments/VisualAttachment';
import { setLastProfileUpdateTimestamp } from '../../util/storage';
import { SessionButton, SessionButtonType } from '../basic/SessionButton'; import { SessionButton, SessionButtonType } from '../basic/SessionButton';
import { SessionSpinner } from '../basic/SessionSpinner'; import { SessionSpinner } from '../basic/SessionSpinner';
import { SessionIconButton } from '../icon'; import { SessionIconButton } from '../icon';
import { SessionWrapperModal } from '../SessionWrapperModal'; import { SessionWrapperModal } from '../SessionWrapperModal';
import { pickFileForAvatar } from '../../types/attachments/VisualAttachment';
import { sanitizeSessionUsername } from '../../session/utils/String';
import { setLastProfileUpdateTimestamp } from '../../util/storage';
import { ConversationTypeEnum } from '../../models/conversationAttributes';
import { MAX_USERNAME_BYTES } from '../../session/constants';
import { SharedConfigMessage } from '../../session/messages/outgoing/controlMessage/SharedConfigMessage';
import { callLibSessionWorker } from '../../webworker/workers/browser/libsession_worker_interface';
import { SignalService } from '../../protobuf';
import Long from 'long';
import { GetNetworkTime } from '../../session/apis/snode_api/getNetworkTime';
import { getMessageQueue } from '../../session/sending';
import { SnodeNamespaces } from '../../session/apis/snode_api/namespaces';
import { from_string } from 'libsodium-wrappers-sumo';
interface State { interface State {
profileName: string; profileName: string;
@ -350,27 +343,8 @@ async function commitProfileEdits(newName: string, scaledAvatarUrl: string | nul
await conversation.commit(); await conversation.commit();
if (window.sessionFeatureFlags.useSharedUtilForUserConfig) { if (window.sessionFeatureFlags.useSharedUtilForUserConfig) {
await callLibSessionWorker(['UserConfig', 'setName', newName]); await ConfigurationSync.queueNewJobIfNeeded();
const pointer = conversation.get('avatarPointer'); await setLastProfileUpdateTimestamp(Date.now());
const profileKey = conversation.get('profileKey');
if (profileKey && pointer) {
await callLibSessionWorker([
'UserConfig',
'setProfilePicture',
pointer,
from_string(profileKey),
]);
} else {
await callLibSessionWorker(['UserConfig', 'setProfilePicture', '', new Uint8Array()]);
}
const message = new SharedConfigMessage({
data: (await callLibSessionWorker(['UserConfig', 'dump'])) as Uint8Array,
kind: SignalService.SharedConfigMessage.Kind.USER_PROFILE,
seqno: Long.fromNumber(0),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
});
await getMessageQueue().sendSyncMessage({ message, namespace: SnodeNamespaces.UserProfile });
} else { } else {
await setLastProfileUpdateTimestamp(Date.now()); await setLastProfileUpdateTimestamp(Date.now());

@ -44,7 +44,7 @@ import { LeftPaneSectionContainer } from './LeftPaneSectionContainer';
import { getLatestReleaseFromFileServer } from '../../session/apis/file_server_api/FileServerApi'; import { getLatestReleaseFromFileServer } from '../../session/apis/file_server_api/FileServerApi';
import { forceRefreshRandomSnodePool } from '../../session/apis/snode_api/snodePool'; import { forceRefreshRandomSnodePool } from '../../session/apis/snode_api/snodePool';
import { initializeLibSessionUtilWrappers } from '../../session/utils/libsession/libsession_utils'; import { LibSessionUtil } from '../../session/utils/libsession/libsession_utils';
import { isDarkTheme } from '../../state/selectors/theme'; import { isDarkTheme } from '../../state/selectors/theme';
import { ThemeStateType } from '../../themes/constants/colors'; import { ThemeStateType } from '../../themes/constants/colors';
import { switchThemeTo } from '../../themes/switchTheme'; import { switchThemeTo } from '../../themes/switchTheme';
@ -193,7 +193,7 @@ const triggerAvatarReUploadIfNeeded = async () => {
* This function is called only once: on app startup with a logged in user * This function is called only once: on app startup with a logged in user
*/ */
const doAppStartUp = async () => { const doAppStartUp = async () => {
await initializeLibSessionUtilWrappers(); await LibSessionUtil.initializeLibSessionUtilWrappers();
void setupTheme(); void setupTheme();
// this generates the key to encrypt attachments locally // this generates the key to encrypt attachments locally
@ -201,6 +201,8 @@ const doAppStartUp = async () => {
await runners.avatarDownloadRunner.loadJobsFromDb(); await runners.avatarDownloadRunner.loadJobsFromDb();
runners.avatarDownloadRunner.startProcessing(); runners.avatarDownloadRunner.startProcessing();
await runners.configurationSyncRunner.loadJobsFromDb();
runners.configurationSyncRunner.startProcessing();
// trigger a sync message if needed for our other devices // trigger a sync message if needed for our other devices
void triggerSyncIfNeeded(); void triggerSyncIfNeeded();

@ -1,41 +1,31 @@
import { import {
AsyncWrapper, AsyncObjectWrapper,
ConfigDumpDataNode,
ConfigDumpRow, ConfigDumpRow,
GetAllDumps, ConfigDumpRowWithoutData,
GetByPubkeyConfigDump,
GetByVariantAndPubkeyConfigDump,
SaveConfigDump,
} from '../../types/sqlSharedTypes'; } from '../../types/sqlSharedTypes';
import { ConfigWrapperObjectTypes } from '../../webworker/workers/browser/libsession_worker_functions'; import { ConfigWrapperObjectTypes } from '../../webworker/workers/browser/libsession_worker_functions';
import { channels } from '../channels'; import { channels } from '../channels';
const getByVariantAndPubkey: AsyncWrapper<GetByVariantAndPubkeyConfigDump> = ( export const ConfigDumpData: AsyncObjectWrapper<ConfigDumpDataNode> = {
variant: ConfigWrapperObjectTypes, getByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, pubkey: string) => {
pubkey: string return channels.getByVariantAndPubkey(variant, pubkey);
) => { },
return channels.getConfigDumpByVariantAndPubkey(variant, pubkey); getMessageHashesByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, pubkey: string) => {
}; return channels.getMessageHashesByVariantAndPubkey(variant, pubkey);
},
const getByPubkey: AsyncWrapper<GetByPubkeyConfigDump> = (pubkey: string) => { saveConfigDump: (dump: ConfigDumpRow) => {
return channels.getConfigDumpsByPk(pubkey); console.warn('saveConfigDump', dump);
}; return channels.saveConfigDump(dump);
},
const saveConfigDump: AsyncWrapper<SaveConfigDump> = (dump: ConfigDumpRow) => { saveCombinedMessageHashesForMatching: (dump: ConfigDumpRowWithoutData) => {
return channels.saveConfigDump(dump); console.warn('saveCombinedMessageHashesForMatching', dump);
}; return channels.saveCombinedMessageHashesForMatching(dump);
},
const getAllDumpsWithData: AsyncWrapper<GetAllDumps> = () => { getAllDumpsWithData: () => {
return channels.getAllDumpsWithData(); return channels.getAllDumpsWithData();
}; },
getAllDumpsWithoutData: () => {
const getAllDumpsWithoutData: AsyncWrapper<GetAllDumps> = () => { return channels.getAllDumpsWithoutData();
return channels.getAllDumpsWithoutData(); },
};
export const ConfigDumpData = {
getByVariantAndPubkey,
getByPubkey,
saveConfigDump,
getAllDumpsWithData,
getAllDumpsWithoutData,
}; };

@ -40,6 +40,7 @@ import { setLastProfileUpdateTimestamp } from '../util/storage';
import { getSodiumRenderer } from '../session/crypto'; import { getSodiumRenderer } from '../session/crypto';
import { encryptProfile } from '../util/crypto/profileEncrypter'; import { encryptProfile } from '../util/crypto/profileEncrypter';
import { uploadFileToFsWithOnionV4 } from '../session/apis/file_server_api/FileServerApi'; import { uploadFileToFsWithOnionV4 } from '../session/apis/file_server_api/FileServerApi';
import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob';
export const getCompleteUrlForV2ConvoId = async (convoId: string) => { export const getCompleteUrlForV2ConvoId = async (convoId: string) => {
if (convoId.match(openGroupV2ConversationIdRegex)) { if (convoId.match(openGroupV2ConversationIdRegex)) {
@ -449,7 +450,11 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) {
if (newAvatarDecrypted) { if (newAvatarDecrypted) {
await setLastProfileUpdateTimestamp(Date.now()); await setLastProfileUpdateTimestamp(Date.now());
await SyncUtils.forceSyncConfigurationNowIfNeeded(true); if (window.sessionFeatureFlags.useSharedUtilForUserConfig) {
await ConfigurationSync.queueNewJobIfNeeded();
} else {
await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}
} else { } else {
window.log.info( window.log.info(
`Reuploading avatar finished at ${newTimestampReupload}, newAttachmentPointer ${fileUrl}` `Reuploading avatar finished at ${newTimestampReupload}, newAttachmentPointer ${fileUrl}`

@ -1247,6 +1247,10 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite
// ; // ;
// `); // `);
// for manually flagging conversations as :unread"
db.exec(`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN markedAsUnread BOOLEAN;`);
// Didn't find any reference to this serverTimestamp in the unprocessed table needed.
db.exec(`ALTER TABLE unprocessed DROP COLUMN serverTimestamp;`); db.exec(`ALTER TABLE unprocessed DROP COLUMN serverTimestamp;`);
// we need to populate those fields with the current state of the conversation so let's throw null until this is done // we need to populate those fields with the current state of the conversation so let's throw null until this is done

@ -48,8 +48,6 @@ import {
} from './database_utility'; } from './database_utility';
import { import {
ConfigDumpDataNode,
ConfigDumpRow,
MsgDuplicateSearchOpenGroup, MsgDuplicateSearchOpenGroup,
UnprocessedDataNode, UnprocessedDataNode,
UnprocessedParameter, UnprocessedParameter,
@ -70,7 +68,7 @@ import {
initDbInstanceWith, initDbInstanceWith,
isInstanceInitialized, isInstanceInitialized,
} from './sqlInstance'; } from './sqlInstance';
import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions'; import { configDumpData } from './sql_calls/config_dump';
// tslint:disable: no-console function-name non-literal-fs-path // tslint:disable: no-console function-name non-literal-fs-path
@ -1964,92 +1962,6 @@ function removeV2OpenGroupRoom(conversationId: string) {
}); });
} }
/**
* Config dumps sql calls
*/
const configDumpData: ConfigDumpDataNode = {
getConfigDumpByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, pubkey: string) => {
const rows = assertGlobalInstance()
.prepare('SELECT * from configDump WHERE variant = $variant AND pubkey = $pubkey;')
.get({
pubkey,
variant,
});
if (!rows) {
return [];
}
throw new Error(`getConfigDumpByVariantAndPubkey: rows: ${JSON.stringify(rows)} `);
return rows;
},
getConfigDumpsByPubkey: (pubkey: string) => {
const rows = assertGlobalInstance()
.prepare('SELECT * from configDump WHERE pubkey = $pubkey;')
.get({
pubkey,
});
if (!rows) {
return [];
}
throw new Error(`getConfigDumpsByPubkey: rows: ${JSON.stringify(rows)} `);
return rows;
},
saveConfigDump: ({ data, pubkey, variant, combinedMessageHashes }: ConfigDumpRow) => {
assertGlobalInstance()
.prepare(
`INSERT OR REPLACE INTO configDump (
pubkey,
variant,
combinedMessageHashes,
data
) values (
$pubkey,
$variant,
$combinedMessageHashes,
$data,
);`
)
.run({
pubkey,
variant,
combinedMessageHashes,
data,
});
},
getAllDumpsWithData: () => {
const rows = assertGlobalInstance()
.prepare('SELECT variant, publicKey, combinedMessageHashes, data from configDump;')
.get();
if (!rows) {
return [];
}
throw new Error(`getAllDumpsWithData: rows: ${JSON.stringify(rows)} `);
return rows;
},
getAllDumpsWithoutData: () => {
const rows = assertGlobalInstance()
.prepare('SELECT variant, publicKey, combinedMessageHashes from configDump;')
.get();
if (!rows) {
return [];
}
throw new Error(`getAllDumpsWithoutData: rows: ${JSON.stringify(rows)} `);
return rows;
},
};
/** /**
* Others * Others
*/ */

@ -0,0 +1,147 @@
/**
* Config dumps sql calls
*/
import { compact, flatten, isEmpty, uniq } from 'lodash';
import {
ConfigDumpDataNode,
ConfigDumpRow,
ConfigDumpRowWithoutData,
} from '../../types/sqlSharedTypes';
import { ConfigWrapperObjectTypes } from '../../webworker/workers/browser/libsession_worker_functions';
import { assertGlobalInstance } from '../sqlInstance';
type CombinedMessageHashes = { combinedMessageHashes?: string };
function parseRow(
row: Pick<ConfigDumpRow, 'data' | 'publicKey' | 'variant'> & CombinedMessageHashes
): ConfigDumpRow | null {
const parsedNoData = parseRowNoData(row);
if (!parsedNoData) {
return null;
}
return { ...parsedNoData, data: row.data };
}
function parseRowNoData(
row: Pick<ConfigDumpRow, 'data' | 'publicKey' | 'variant'> & CombinedMessageHashes
): ConfigDumpRowWithoutData | null {
const toRet: ConfigDumpRowWithoutData = {
publicKey: row.publicKey,
variant: row.variant,
combinedMessageHashes: [],
};
toRet.combinedMessageHashes = parseRowMessageHashes(row);
return toRet;
}
function parseRowMessageHashes(row: CombinedMessageHashes): Array<string> {
if (!isEmpty(row.combinedMessageHashes) && row.combinedMessageHashes) {
try {
return JSON.parse(row.combinedMessageHashes) || [];
} catch (e) {
console.warn('parseRowMessageHashes row failed');
}
}
return [];
}
export const configDumpData: ConfigDumpDataNode = {
getByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, publicKey: string) => {
const rows = assertGlobalInstance()
.prepare(
'SELECT publicKey, variant, combinedMessageHashes, data from configDump WHERE variant = $variant AND publicKey = $publicKey;'
)
.all({
publicKey,
variant,
});
if (!rows) {
return [];
}
return compact(rows.map(parseRow));
},
getMessageHashesByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, publicKey: string) => {
const rows = assertGlobalInstance()
.prepare(
'SELECT combinedMessageHashes from configDump WHERE variant = $variant AND publicKey = $publicKey;'
)
.all({
publicKey,
variant,
});
if (!rows) {
return [];
}
return uniq(flatten(rows.map(parseRowMessageHashes)));
},
getAllDumpsWithData: () => {
const rows = assertGlobalInstance()
.prepare('SELECT variant, publicKey, combinedMessageHashes, data from configDump;')
.all();
if (!rows) {
return [];
}
return compact(rows.map(parseRow));
},
getAllDumpsWithoutData: () => {
const rows = assertGlobalInstance()
.prepare('SELECT variant, publicKey, combinedMessageHashes from configDump;')
.all();
if (!rows) {
return [];
}
return compact(rows.map(parseRowNoData));
},
saveConfigDump: ({ data, publicKey, variant, combinedMessageHashes }: ConfigDumpRow) => {
assertGlobalInstance()
.prepare(
`INSERT OR REPLACE INTO configDump (
publicKey,
variant,
combinedMessageHashes,
data
) values (
$publicKey,
$variant,
$combinedMessageHashes,
$data
);`
)
.run({
publicKey,
variant,
combinedMessageHashes: JSON.stringify(combinedMessageHashes || []),
data,
});
},
saveCombinedMessageHashesForMatching: ({
publicKey,
variant,
combinedMessageHashes,
}: ConfigDumpRowWithoutData) => {
assertGlobalInstance()
.prepare(
`UPDATE configDump SET
combinedMessageHashes = $combinedMessageHashes
WHERE publicKey=$publicKey AND variant=$variant;`
)
.run({
publicKey,
variant,
combinedMessageHashes: JSON.stringify(combinedMessageHashes || []),
});
},
};

@ -1,5 +1,5 @@
import _, { isEmpty } from 'lodash'; import _, { compact, flattenDeep, isEmpty, isEqual } from 'lodash';
import { ContactInfo, ProfilePicture } from 'session_util_wrapper'; import { ConfigDumpData } from '../data/configDump/configDump';
import { Data, hasSyncedInitialConfigurationItem } from '../data/data'; import { Data, hasSyncedInitialConfigurationItem } from '../data/data';
import { ConversationInteraction } from '../interactions'; import { ConversationInteraction } from '../interactions';
import { ConversationTypeEnum } from '../models/conversationAttributes'; import { ConversationTypeEnum } from '../models/conversationAttributes';
@ -13,12 +13,17 @@ import { getConversationController } from '../session/conversations';
import { IncomingMessage } from '../session/messages/incoming/IncomingMessage'; import { IncomingMessage } from '../session/messages/incoming/IncomingMessage';
import { ProfileManager } from '../session/profile_manager/ProfileManager'; import { ProfileManager } from '../session/profile_manager/ProfileManager';
import { UserUtils } from '../session/utils'; import { UserUtils } from '../session/utils';
import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob';
import { toHex } from '../session/utils/String'; import { toHex } from '../session/utils/String';
import { configurationMessageReceived, trigger } from '../shims/events'; import { configurationMessageReceived, trigger } from '../shims/events';
import { BlockedNumberController } from '../util'; import { BlockedNumberController } from '../util';
import { getLastProfileUpdateTimestamp, setLastProfileUpdateTimestamp } from '../util/storage'; import { getLastProfileUpdateTimestamp, setLastProfileUpdateTimestamp } from '../util/storage';
import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions'; import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions';
import { callLibSessionWorker } from '../webworker/workers/browser/libsession_worker_interface'; import {
ContactsWrapperActions,
GenericWrapperActions,
UserConfigWrapperActions,
} from '../webworker/workers/browser/libsession_worker_interface';
import { removeFromCache } from './cache'; import { removeFromCache } from './cache';
import { handleNewClosedGroup } from './closedGroups'; import { handleNewClosedGroup } from './closedGroups';
import { EnvelopePlus } from './types'; import { EnvelopePlus } from './types';
@ -30,7 +35,7 @@ type IncomingConfResult = {
latestSentTimestamp: number; latestSentTimestamp: number;
}; };
function protobufSharedConfigTypeToWrapper( function pbKindToVariant(
kind: SignalService.SharedConfigMessage.Kind kind: SignalService.SharedConfigMessage.Kind
): ConfigWrapperObjectTypes | null { ): ConfigWrapperObjectTypes | null {
switch (kind) { switch (kind) {
@ -45,59 +50,44 @@ function protobufSharedConfigTypeToWrapper(
async function mergeConfigsWithIncomingUpdates( async function mergeConfigsWithIncomingUpdates(
incomingConfig: IncomingMessage<SignalService.ISharedConfigMessage> incomingConfig: IncomingMessage<SignalService.ISharedConfigMessage>
) { ): Promise<{ kind: SignalService.SharedConfigMessage.Kind; result: IncomingConfResult }> {
const kindMessageMap: Map<SignalService.SharedConfigMessage.Kind, IncomingConfResult> = new Map(); const { kind } = incomingConfig.message;
const allKinds = [incomingConfig.message.kind];
for (let index = 0; index < allKinds.length; index++) {
const kind = allKinds[index];
const currentKindMessages = [incomingConfig];
if (!currentKindMessages) {
continue;
}
const toMerge = currentKindMessages.map(m => m.message.data);
const wrapperId = protobufSharedConfigTypeToWrapper(kind); const toMerge = [incomingConfig.message.data];
if (!wrapperId) {
throw new Error(`Invalid kind: ${kind}`);
}
await callLibSessionWorker([wrapperId, 'merge', toMerge]); const wrapperId = pbKindToVariant(kind);
const needsPush = ((await callLibSessionWorker([wrapperId, 'needsPush'])) || false) as boolean; if (!wrapperId) {
const needsDump = ((await callLibSessionWorker([wrapperId, 'needsDump'])) || false) as boolean; throw new Error(`Invalid kind: ${kind}`);
const messageHashes = currentKindMessages.map(m => m.messageHash);
const latestSentTimestamp = Math.max(...currentKindMessages.map(m => m.envelopeTimestamp));
const incomingConfResult: IncomingConfResult = {
latestSentTimestamp,
messageHashes,
needsDump,
needsPush,
};
kindMessageMap.set(kind, incomingConfResult);
} }
return kindMessageMap; await GenericWrapperActions.merge(wrapperId, toMerge);
const needsPush = await GenericWrapperActions.needsPush(wrapperId);
const needsDump = await GenericWrapperActions.needsDump(wrapperId);
const messageHashes = [incomingConfig.messageHash];
const latestSentTimestamp = incomingConfig.envelopeTimestamp;
const incomingConfResult: IncomingConfResult = {
latestSentTimestamp,
messageHashes,
needsDump,
needsPush,
};
return { kind, result: incomingConfResult };
} }
async function handleUserProfileUpdate(result: IncomingConfResult) { async function handleUserProfileUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {
const updatedUserName = await UserConfigWrapperActions.getName();
console.warn('got', updatedUserName);
if (result.needsDump) { if (result.needsDump) {
return; return result;
} }
const updatedUserName = (await callLibSessionWorker(['UserConfig', 'getName'])) as if (!updatedUserName) {
| string debugger;
| undefined;
const updatedProfilePicture = (await callLibSessionWorker([
'UserConfig',
'getProfilePicture',
])) as ProfilePicture;
// fetch our own conversation
const userPublicKey = UserUtils.getOurPubKeyStrFromCache();
if (!userPublicKey) {
return;
} }
const updatedProfilePicture = await UserConfigWrapperActions.getProfilePicture();
const picUpdate = !isEmpty(updatedProfilePicture.key) && !isEmpty(updatedProfilePicture.url); const picUpdate = !isEmpty(updatedProfilePicture.key) && !isEmpty(updatedProfilePicture.url);
@ -106,16 +96,15 @@ async function handleUserProfileUpdate(result: IncomingConfResult) {
picUpdate ? updatedProfilePicture.url : null, picUpdate ? updatedProfilePicture.url : null,
picUpdate ? updatedProfilePicture.key : null picUpdate ? updatedProfilePicture.key : null
); );
return result;
} }
async function handleContactsUpdate(result: IncomingConfResult) { async function handleContactsUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {
if (result.needsDump) { if (result.needsDump) {
return; return result;
} }
const allContacts = (await callLibSessionWorker(['ContactsConfig', 'getAll'])) as Array< const allContacts = await ContactsWrapperActions.getAll();
ContactInfo
>;
for (let index = 0; index < allContacts.length; index++) { for (let index = 0; index < allContacts.length; index++) {
const wrapperConvo = allContacts[index]; const wrapperConvo = allContacts[index];
@ -161,7 +150,7 @@ async function handleContactsUpdate(result: IncomingConfResult) {
await existingConvo.commit(); await existingConvo.commit();
} }
// we still need to handle the the `name` (sync) and the `profilePicture` (asynchronous) // we still need to handle the the `name` (synchronous) and the `profilePicture` (asynchronous)
await ProfileManager.updateProfileOfContact( await ProfileManager.updateProfileOfContact(
existingConvo.id, existingConvo.id,
wrapperConvo.name, wrapperConvo.name,
@ -170,36 +159,85 @@ async function handleContactsUpdate(result: IncomingConfResult) {
); );
} }
} }
return result;
} }
async function processMergingResults( async function processMergingResults(
envelope: EnvelopePlus, envelope: EnvelopePlus,
results: Map<SignalService.SharedConfigMessage.Kind, IncomingConfResult> result: { kind: SignalService.SharedConfigMessage.Kind; result: IncomingConfResult }
) { ) {
const keys = [...results.keys()]; const pubkey = envelope.source;
for (let index = 0; index < keys.length; index++) { const { kind, result: incomingResult } = result;
const kind = keys[index];
const result = results.get(kind);
if (!result) { if (!incomingResult) {
continue; await removeFromCache(envelope);
} return;
}
try { try {
switch (kind) { let finalResult = incomingResult;
case SignalService.SharedConfigMessage.Kind.USER_PROFILE:
await handleUserProfileUpdate(result); switch (kind) {
break; case SignalService.SharedConfigMessage.Kind.USER_PROFILE:
case SignalService.SharedConfigMessage.Kind.CONTACTS: finalResult = await handleUserProfileUpdate(incomingResult);
await handleContactsUpdate(result); break;
break; case SignalService.SharedConfigMessage.Kind.CONTACTS:
} finalResult = await handleContactsUpdate(incomingResult);
} catch (e) { break;
throw e; default:
throw new Error(`processMergingResults unknown kind of contact : ${kind}`);
}
const variant = pbKindToVariant(kind);
if (!variant) {
throw new Error('unknown variant');
}
// We need to get the existing message hashes and combine them with the latest from the
// service node to ensure the next push will properly clean up old messages
const oldMessageHashesWithDup = (
await ConfigDumpData.getByVariantAndPubkey(variant, envelope.source)
).map(m => m.combinedMessageHashes);
const oldMessageHashes = new Set(...flattenDeep(compact(oldMessageHashesWithDup)));
const allMessageHashes = new Set([...oldMessageHashes, ...finalResult.messageHashes]);
const finalResultsHashes = new Set([...finalResult.messageHashes]);
// lodash does deep compare of Sets
const messageHashesChanged = !isEqual(oldMessageHashes, finalResultsHashes);
if (finalResult.needsDump) {
// The config data had changes so regenerate the dump and save it
const dump = await GenericWrapperActions.dump(variant);
await ConfigDumpData.saveConfigDump({
data: dump,
publicKey: pubkey,
variant,
combinedMessageHashes: [...allMessageHashes],
});
} else if (messageHashesChanged) {
// The config data didn't change but there were different messages on the service node
// so just update the message hashes so the next sync can properly remove any old ones
await ConfigDumpData.saveCombinedMessageHashesForMatching({
publicKey: pubkey,
variant,
combinedMessageHashes: [...allMessageHashes],
});
} }
console.warn('all dumps in DB: ', await ConfigDumpData.getAllDumpsWithoutData());
await removeFromCache(envelope);
} catch (e) {
window.log.error(`processMergingResults failed with ${e.message}`);
await removeFromCache(envelope);
return;
} }
await removeFromCache(envelope); await removeFromCache(envelope);
// Now that the local state has been updated, trigger a config sync (this will push any
// pending updates and properly update the state)
if (result.result.needsPush) {
await ConfigurationSync.queueNewJobIfNeeded();
}
} }
async function handleConfigMessageViaLibSession( async function handleConfigMessageViaLibSession(
@ -218,11 +256,11 @@ async function handleConfigMessageViaLibSession(
return; return;
} }
window?.log?.info(`Handling our profileUdpates via libsession_util.`); window?.log?.info('Handling our profileUdpates via libsession_util.');
const kindMessagesMap = await mergeConfigsWithIncomingUpdates(configMessage); const incomingMergeResult = await mergeConfigsWithIncomingUpdates(configMessage);
await processMergingResults(envelope, kindMessagesMap); await processMergingResults(envelope, incomingMergeResult);
} }
async function handleOurProfileUpdate( async function handleOurProfileUpdate(

@ -231,7 +231,7 @@ export class MessageQueue {
pubkey, pubkey,
}: { }: {
pubkey: PubKey; pubkey: PubKey;
message: ClosedGroupNewMessage | CallMessage; message: ClosedGroupNewMessage | CallMessage | SharedConfigMessage;
namespace: SnodeNamespaces; namespace: SnodeNamespaces;
}): Promise<boolean | number> { }): Promise<boolean | number> {
let rawMessage; let rawMessage;
@ -258,6 +258,7 @@ export class MessageQueue {
*/ */
public async processPending(device: PubKey, isSyncMessage: boolean = false) { public async processPending(device: PubKey, isSyncMessage: boolean = false) {
const messages = await this.pendingMessageCache.getForDevice(device); const messages = await this.pendingMessageCache.getForDevice(device);
console.warn('processPending', messages);
const jobQueue = this.getJobQueue(device); const jobQueue = this.getJobQueue(device);
messages.forEach(async message => { messages.forEach(async message => {

@ -124,7 +124,7 @@ export async function send(
? SnodeNamespaces.ClosedGroupMessage ? SnodeNamespaces.ClosedGroupMessage
: SnodeNamespaces.UserMessages; : SnodeNamespaces.UserMessages;
} }
let timestamp = networkTimestamp; const timestamp = networkTimestamp;
// the user config namespaces requires a signature to be added // the user config namespaces requires a signature to be added
let signOpts: SnodeSignatureResult | undefined; let signOpts: SnodeSignatureResult | undefined;
if (SnodeNamespace.isUserConfigNamespace(namespace)) { if (SnodeNamespace.isUserConfigNamespace(namespace)) {
@ -247,7 +247,7 @@ export async function sendMessageToSnode({
window?.log?.info( window?.log?.info(
`loki_message:::sendMessage - Successfully stored message to ${ed25519Str(pubKey)} via ${ `loki_message:::sendMessage - Successfully stored message to ${ed25519Str(pubKey)} via ${
snode.ip snode.ip
}:${snode.port}` }:${snode.port} on namespace: ${namespace}`
); );
} }
} }

@ -4,7 +4,7 @@ import {
FakeSleepForMultiJob, FakeSleepForMultiJob,
} from '../../../test/session/unit/utils/job_runner/FakeSleepForJob'; } from '../../../test/session/unit/utils/job_runner/FakeSleepForJob';
import { AvatarDownload } from './jobs/AvatarDownloadJob'; import { AvatarDownload } from './jobs/AvatarDownloadJob';
import { ConfigurationSyncJob } from './jobs/ConfigurationSyncJob'; import { ConfigurationSync } from './jobs/ConfigurationSyncJob';
import { PersistedJob, TypeOfPersistedData } from './PersistedJob'; import { PersistedJob, TypeOfPersistedData } from './PersistedJob';
export function persistedJobFromData<T extends TypeOfPersistedData>( export function persistedJobFromData<T extends TypeOfPersistedData>(
@ -16,7 +16,7 @@ export function persistedJobFromData<T extends TypeOfPersistedData>(
switch (data.jobType) { switch (data.jobType) {
case 'ConfigurationSyncJobType': case 'ConfigurationSyncJobType':
return (new ConfigurationSyncJob(data) as unknown) as PersistedJob<T>; return (new ConfigurationSync.ConfigurationSyncJob(data) as unknown) as PersistedJob<T>;
case 'AvatarDownloadJobType': case 'AvatarDownloadJobType':
return (new AvatarDownload.AvatarDownloadJob(data) as unknown) as PersistedJob<T>; return (new AvatarDownload.AvatarDownloadJob(data) as unknown) as PersistedJob<T>;

@ -1,5 +1,17 @@
import { from_string } from 'libsodium-wrappers-sumo';
import { isNumber } from 'lodash';
import Long from 'long';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { sleepFor } from '../../Promise'; import { UserUtils } from '../..';
import { SignalService } from '../../../../protobuf';
import { UserConfigWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import { GetNetworkTime } from '../../../apis/snode_api/getNetworkTime';
import { SnodeNamespaces } from '../../../apis/snode_api/namespaces';
import { getConversationController } from '../../../conversations';
import { SharedConfigMessage } from '../../../messages/outgoing/controlMessage/SharedConfigMessage';
import { getMessageQueue } from '../../../sending';
import { PubKey } from '../../../types';
import { runners } from '../JobRunner';
import { import {
AddJobCheckReturn, AddJobCheckReturn,
ConfigurationSyncPersistedData, ConfigurationSyncPersistedData,
@ -8,36 +20,76 @@ import {
} from '../PersistedJob'; } from '../PersistedJob';
const defaultMsBetweenRetries = 3000; const defaultMsBetweenRetries = 3000;
const defaultMaxAttempts = 3;
export class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersistedData> { class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersistedData> {
constructor({ constructor({
identifier, identifier,
nextAttemptTimestamp, nextAttemptTimestamp,
maxAttempts, maxAttempts,
currentRetry, currentRetry,
}: Pick<ConfigurationSyncPersistedData, 'identifier' | 'currentRetry' | 'maxAttempts'> & }: Partial<
Partial<Pick<ConfigurationSyncPersistedData, 'nextAttemptTimestamp'>>) { Pick<
ConfigurationSyncPersistedData,
'identifier' | 'nextAttemptTimestamp' | 'currentRetry' | 'maxAttempts'
>
>) {
super({ super({
jobType: 'ConfigurationSyncJobType', jobType: 'ConfigurationSyncJobType',
identifier: identifier || v4(), identifier: identifier || v4(),
delayBetweenRetries: defaultMsBetweenRetries, delayBetweenRetries: defaultMsBetweenRetries,
maxAttempts: maxAttempts, maxAttempts: isNumber(maxAttempts) ? maxAttempts : defaultMaxAttempts,
currentRetry: isNumber(currentRetry) ? currentRetry : 0,
nextAttemptTimestamp: nextAttemptTimestamp || Date.now() + defaultMsBetweenRetries, nextAttemptTimestamp: nextAttemptTimestamp || Date.now() + defaultMsBetweenRetries,
currentRetry,
}); });
} }
public async run(): Promise<RunJobResult> { public async run(): Promise<RunJobResult> {
// blablha do everything from the notion page, and if success, return true. window.log.debug(`ConfigurationSyncJob starting ${this.persistedData.identifier}`);
window.log.warn(
`running job ${this.persistedData.jobType} with id:"${this.persistedData.identifier}" ` const us = UserUtils.getOurPubKeyStrFromCache();
); const conversation = getConversationController().get(us);
if (!us || !conversation) {
window.log.warn('did not find our own conversation');
return RunJobResult.PermanentFailure;
}
const name = conversation.get('displayNameInProfile');
const pointer = conversation.get('avatarPointer');
const profileKey = conversation.get('profileKey');
await UserConfigWrapperActions.setName(name || '');
if (profileKey && pointer) {
await UserConfigWrapperActions.setProfilePicture(pointer, from_string(profileKey));
} else {
await UserConfigWrapperActions.setProfilePicture('', new Uint8Array());
}
const data = await UserConfigWrapperActions.push();
const message = new SharedConfigMessage({
data: data.data,
kind: SignalService.SharedConfigMessage.Kind.USER_PROFILE,
seqno: Long.fromNumber(data.seqno),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
});
await sleepFor(5000); const result = await getMessageQueue().sendToPubKeyNonDurably({
window.log.warn( message,
`running job ${this.persistedData.jobType} with id:"${this.persistedData.identifier}" done and returning failed ` namespace: SnodeNamespaces.UserProfile,
pubkey: PubKey.cast(us),
});
console.warn(
`ConfigurationSyncJob sendToPubKeyNonDurably ${this.persistedData.identifier} returned: "${result}"`
); );
if (isNumber(result)) {
// try {
// markAsPushed
// }
debugger;
return RunJobResult.Success;
}
return RunJobResult.RetryJobIfPossible; return RunJobResult.RetryJobIfPossible;
} }
@ -59,3 +111,16 @@ export class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersiste
return []; return [];
} }
} }
/**
* Queue a new Sync Configuration if needed job.
* A ConfigurationSyncJob can only be added if there is none of the same type queued already.
*/
async function queueNewJobIfNeeded() {
await runners.configurationSyncRunner.addJob(new ConfigurationSyncJob({}));
}
export const ConfigurationSync = {
ConfigurationSyncJob,
queueNewJobIfNeeded,
};

@ -1,28 +1,77 @@
import { difference } from 'lodash'; import { difference, isEqual } from 'lodash';
import { UserUtils } from '..'; import { UserUtils } from '..';
import { ConfigDumpData } from '../../../data/configDump/configDump'; import { ConfigDumpData } from '../../../data/configDump/configDump';
import { ConfigWrapperObjectTypes } from '../../../webworker/workers/browser/libsession_worker_functions'; import { ConfigWrapperObjectTypes } from '../../../webworker/workers/browser/libsession_worker_functions';
import { callLibSessionWorker } from '../../../webworker/workers/browser/libsession_worker_interface'; import {
GenericWrapperActions,
UserConfigWrapperActions,
} from '../../../webworker/workers/browser/libsession_worker_interface';
import { getConversationController } from '../../conversations';
import { ConfigurationSync } from '../job_runners/jobs/ConfigurationSyncJob';
export async function initializeLibSessionUtilWrappers() { // TODO complete this list
const requiredVariants: Array<ConfigWrapperObjectTypes> = ['UserConfig', 'ContactsConfig']; // 'conversations'
async function insertUserProfileIntoWrapper() {
const us = UserUtils.getOurPubKeyStrFromCache();
const ourConvo = getConversationController().get(us);
if (!ourConvo) {
throw new Error('insertUserProfileIntoWrapper needs a ourConvo to exist');
}
const currentWrapperName = await UserConfigWrapperActions.getName();
const currentWrapperProfileUrl = await UserConfigWrapperActions.getProfilePicture();
const currentDbName = ourConvo.get('displayNameInProfile') || '';
if (!isEqual(currentDbName, currentWrapperName)) {
await UserConfigWrapperActions.setName(currentDbName);
}
}
/**
* Right after we migrated, we won't have any dumps in DB. We must create them from our database state,
*/
async function createConfigDumpsFromDbFirstStart(privateKeyEd25519: Uint8Array) {
let countCreated = 0;
try {
// build the userconfig
await UserConfigWrapperActions.init(privateKeyEd25519, null);
countCreated++;
} catch (e) {
window.log.warn('Failed to init the UserConfig with createConfigDumpsFromDbFirstStart');
}
if (countCreated > 0) {
await ConfigurationSync.queueNewJobIfNeeded();
}
}
async function initializeLibSessionUtilWrappers() {
const keypair = await UserUtils.getUserED25519KeyPairBytes(); const keypair = await UserUtils.getUserED25519KeyPairBytes();
if (!keypair) { if (!keypair || !keypair.privKeyBytes) {
throw new Error('edkeypair not found for current user'); throw new Error('edkeypair not found for current user');
} }
const privateKeyEd25519 = keypair.privKeyBytes; const privateKeyEd25519 = keypair.privKeyBytes;
const dumps = await ConfigDumpData.getAllDumpsWithData(); let dumps = await ConfigDumpData.getAllDumpsWithData();
if (!dumps?.length) {
await createConfigDumpsFromDbFirstStart(privateKeyEd25519);
}
// refetch them as the createConfigDumpsFromDb might have created them
dumps = await ConfigDumpData.getAllDumpsWithData();
const userVariantsBuildWithoutErrors = new Set<ConfigWrapperObjectTypes>(); const userVariantsBuildWithoutErrors = new Set<ConfigWrapperObjectTypes>();
for (let index = 0; index < dumps.length; index++) { for (let index = 0; index < dumps.length; index++) {
const dump = dumps[index]; const dump = dumps[index];
try { try {
await callLibSessionWorker([ await GenericWrapperActions.init(
dump.variant, dump.variant,
'init',
privateKeyEd25519, privateKeyEd25519,
dump.data.length ? dump.data : null, dump.data.length ? dump.data : null
]); );
userVariantsBuildWithoutErrors.add(dump.variant); userVariantsBuildWithoutErrors.add(dump.variant);
} catch (e) { } catch (e) {
@ -31,14 +80,16 @@ export async function initializeLibSessionUtilWrappers() {
} }
} }
// TODO complete this list console.warn('requiredVariants: FIXME add conversation volatile wrapper as required ');
const requiredVariants: Array<ConfigWrapperObjectTypes> = ['UserConfig', 'ContactsConfig']; // 'conversations'
const missingRequiredVariants: Array<ConfigWrapperObjectTypes> = difference(requiredVariants, [ const missingRequiredVariants: Array<ConfigWrapperObjectTypes> = difference(requiredVariants, [
...userVariantsBuildWithoutErrors.values(), ...userVariantsBuildWithoutErrors.values(),
]); ]);
for (let index = 0; index < missingRequiredVariants.length; index++) { for (let index = 0; index < missingRequiredVariants.length; index++) {
const missingVariant = missingRequiredVariants[index]; const missingVariant = missingRequiredVariants[index];
await callLibSessionWorker([missingVariant, 'init', privateKeyEd25519, null]); await GenericWrapperActions.init(missingVariant, privateKeyEd25519, null);
} }
} }
export const LibSessionUtil = { initializeLibSessionUtilWrappers };

@ -35,7 +35,6 @@ describe('libsession_wrapper', () => {
expect(pushResult.seqno).to.be.eq(0); expect(pushResult.seqno).to.be.eq(0);
expect(pushResult.data.length).to.be.eq(256); expect(pushResult.data.length).to.be.eq(256);
expect(conf.encryptionDomain()).to.be.eq('UserProfile');
expect(conf.storageNamespace()).to.be.eq(2); expect(conf.storageNamespace()).to.be.eq(2);
expect(to_hex(pushResult.data)).to.be.deep.eq( expect(to_hex(pushResult.data)).to.be.deep.eq(
'9ffb5347e061ac40d937ae4f1a890031475bdc11653f94c8ae1d516ffda71d9ee9cdaf9fbaeb15d835cdc7b3b6ecc120361f004ff172dd5e757c80ede10e88945536e6841255a7bca73664ab8a0607fcfe2579c05bb3d9d4b34ac1de2921e703783ce39e317a512cb9d4e3b59176cbde47b5ba24a03065bf8fefe3e8ca2609e0ad10c7c9c3f81dc6d3a399bda0c190e8a228d0acb22863ab84c2d0c411be74dac4de1f8bc18539635db01ea1ef7f28e505703d67786cb419690edd4bd8c92926fc1d6449eaccc31d7d9639e1b36222e5672b87d1e34b7860308c3f40b3997f39fecf6ceb889323826fa69e001816307799fc9fed302a90faa1e43f7cd7367c3c' '9ffb5347e061ac40d937ae4f1a890031475bdc11653f94c8ae1d516ffda71d9ee9cdaf9fbaeb15d835cdc7b3b6ecc120361f004ff172dd5e757c80ede10e88945536e6841255a7bca73664ab8a0607fcfe2579c05bb3d9d4b34ac1de2921e703783ce39e317a512cb9d4e3b59176cbde47b5ba24a03065bf8fefe3e8ca2609e0ad10c7c9c3f81dc6d3a399bda0c190e8a228d0acb22863ab84c2d0c411be74dac4de1f8bc18539635db01ea1ef7f28e505703d67786cb419690edd4bd8c92926fc1d6449eaccc31d7d9639e1b36222e5672b87d1e34b7860308c3f40b3997f39fecf6ceb889323826fa69e001816307799fc9fed302a90faa1e43f7cd7367c3c'

@ -9,6 +9,13 @@ export type AsyncWrapper<T extends (...args: any) => any> = (
...args: Parameters<T> ...args: Parameters<T>
) => Promise<ReturnType<T>>; ) => Promise<ReturnType<T>>;
/**
* This type is used to build from an objectType filled with functions, a new object type where all the functions their async equivalent
*/
export type AsyncObjectWrapper<Type extends Record<string, (...args: any) => any>> = {
[Property in keyof Type]: AsyncWrapper<Type[Property]>;
};
export type MsgDuplicateSearchOpenGroup = Array<{ export type MsgDuplicateSearchOpenGroup = Array<{
sender: string; sender: string;
serverTimestamp: number; serverTimestamp: number;
@ -25,28 +32,33 @@ export type UpdateLastHashType = {
export type ConfigDumpRow = { export type ConfigDumpRow = {
variant: ConfigWrapperObjectTypes; // the variant this entry is about. (user pr, contacts, ...) variant: ConfigWrapperObjectTypes; // the variant this entry is about. (user pr, contacts, ...)
pubkey: string; // either our pubkey if a dump for our own swarm or the closed group pubkey publicKey: string; // either our pubkey if a dump for our own swarm or the closed group pubkey
data: Uint8Array; // the blob returned by libsession.dump() call data: Uint8Array; // the blob returned by libsession.dump() call
combinedMessageHashes?: string; // array of lastHashes to keep track of, stringified combinedMessageHashes: Array<string>; // array of lastHashes to keep track of
// we might need to add a `seqno` field here. // we might need to add a `seqno` field here.
}; };
// ========== configdump export type ConfigDumpRowWithoutData = Pick<
ConfigDumpRow,
'publicKey' | 'combinedMessageHashes' | 'variant'
>;
export type GetByVariantAndPubkeyConfigDump = ( // ========== configdump
variant: ConfigWrapperObjectTypes,
pubkey: string
) => Array<ConfigDumpRow>;
export type GetByPubkeyConfigDump = (pubkey: string) => Array<ConfigDumpRow>;
export type SaveConfigDump = (dump: ConfigDumpRow) => void;
export type GetAllDumps = () => Array<ConfigDumpRow>;
export type ConfigDumpDataNode = { export type ConfigDumpDataNode = {
getConfigDumpByVariantAndPubkey: GetByVariantAndPubkeyConfigDump; getByVariantAndPubkey: (
getConfigDumpsByPubkey: GetByPubkeyConfigDump; variant: ConfigWrapperObjectTypes,
saveConfigDump: SaveConfigDump; publicKey: string
getAllDumpsWithData: GetAllDumps; ) => Array<ConfigDumpRow>;
getAllDumpsWithoutData: GetAllDumps; getMessageHashesByVariantAndPubkey: (
variant: ConfigWrapperObjectTypes,
publicKey: string
) => Array<string>;
saveConfigDump: (dump: ConfigDumpRow) => void;
saveCombinedMessageHashesForMatching: (row: ConfigDumpRowWithoutData) => void;
getAllDumpsWithData: () => Array<ConfigDumpRow>;
getAllDumpsWithoutData: () => Array<ConfigDumpRowWithoutData>;
}; };
// ========== unprocessed // ========== unprocessed

@ -1,6 +1,5 @@
import { import {
BaseConfigActions, BaseConfigActions,
BaseConfigWrapper,
ContactsConfigActionsType, ContactsConfigActionsType,
UserConfigActionsType, UserConfigActionsType,
} from 'session_util_wrapper'; } from 'session_util_wrapper';
@ -20,9 +19,4 @@ type ContactsConfigFunctions =
| [ContactsConfig, ...BaseConfigActions] | [ContactsConfig, ...BaseConfigActions]
| [ContactsConfig, ...ContactsConfigActionsType]; | [ContactsConfig, ...ContactsConfigActionsType];
/**Those are the actions inherited from BaseConfigWrapper to ClosedGroupConfigWrapper */
// type ClosedGroupConfigFromBase = [ClosedGroupConfig, ...BaseConfigActions];
// type ClosedGroupConfigFunctions = ClosedGroupConfigFromBase;
//| ClosedGroupConfigFunctions;
export type LibSessionWorkerFunctions = UserConfigFunctions | ContactsConfigFunctions; export type LibSessionWorkerFunctions = UserConfigFunctions | ContactsConfigFunctions;

@ -1,7 +1,14 @@
import { WorkerInterface } from '../../worker_interface'; import { WorkerInterface } from '../../worker_interface';
import { join } from 'path'; import { join } from 'path';
import { getAppRootPath } from '../../../node/getRootPath'; import { getAppRootPath } from '../../../node/getRootPath';
import { LibSessionWorkerFunctions } from './libsession_worker_functions'; import { ConfigWrapperObjectTypes, LibSessionWorkerFunctions } from './libsession_worker_functions';
import {
BaseWrapperActionsCalls,
ContactInfo,
ContactsWrapperActionsCalls,
UserConfigWrapperActionsCalls,
} from 'session_util_wrapper';
let libsessionWorkerInterface: WorkerInterface | undefined; let libsessionWorkerInterface: WorkerInterface | undefined;
@ -26,8 +33,138 @@ const internalCallLibSessionWorker = async ([
return libsessionWorkerInterface?.callWorker(config, fnName, ...args); return libsessionWorkerInterface?.callWorker(config, fnName, ...args);
}; };
export const callLibSessionWorker = async ( export const GenericWrapperActions = {
callToMake: LibSessionWorkerFunctions init: async (
): Promise<unknown> => { wrapperId: ConfigWrapperObjectTypes,
ed25519Key: Uint8Array,
dump: Uint8Array | null
) =>
/** base wrapper generic actions */
callLibSessionWorker([wrapperId, 'init', ed25519Key, dump]) as Promise<void>,
confirmPushed: async (wrapperId: ConfigWrapperObjectTypes, seqno: number) =>
callLibSessionWorker([wrapperId, 'confirmPushed', seqno]) as ReturnType<
BaseWrapperActionsCalls['confirmPushed']
>,
dump: async (wrapperId: ConfigWrapperObjectTypes) =>
callLibSessionWorker([wrapperId, 'dump']) as Promise<
ReturnType<BaseWrapperActionsCalls['dump']>
>,
merge: async (wrapperId: ConfigWrapperObjectTypes, toMerge: Array<Uint8Array>) =>
callLibSessionWorker([wrapperId, 'merge', toMerge]) as Promise<
ReturnType<BaseWrapperActionsCalls['merge']>
>,
needsDump: async (wrapperId: ConfigWrapperObjectTypes) =>
callLibSessionWorker([wrapperId, 'needsDump']) as Promise<
ReturnType<BaseWrapperActionsCalls['needsDump']>
>,
needsPush: async (wrapperId: ConfigWrapperObjectTypes) =>
callLibSessionWorker([wrapperId, 'needsPush']) as Promise<
ReturnType<BaseWrapperActionsCalls['needsPush']>
>,
push: async (wrapperId: ConfigWrapperObjectTypes) =>
callLibSessionWorker([wrapperId, 'push']) as Promise<
ReturnType<BaseWrapperActionsCalls['push']>
>,
storageNamespace: async (wrapperId: ConfigWrapperObjectTypes) =>
callLibSessionWorker([wrapperId, 'storageNamespace']) as Promise<
ReturnType<BaseWrapperActionsCalls['storageNamespace']>
>,
};
export const UserConfigWrapperActions: UserConfigWrapperActionsCalls = {
/* Reuse the GenericWrapperActions with the UserConfig argument */
init: async (ed25519Key: Uint8Array, dump: Uint8Array | null) =>
GenericWrapperActions.init('UserConfig', ed25519Key, dump),
confirmPushed: async (seqno: number) => GenericWrapperActions.confirmPushed('UserConfig', seqno),
dump: async () => GenericWrapperActions.dump('UserConfig'),
merge: async (toMerge: Array<Uint8Array>) => GenericWrapperActions.merge('UserConfig', toMerge),
needsDump: async () => GenericWrapperActions.needsDump('UserConfig'),
needsPush: async () => GenericWrapperActions.needsPush('UserConfig'),
push: async () => GenericWrapperActions.push('UserConfig'),
storageNamespace: async () => GenericWrapperActions.storageNamespace('UserConfig'),
/** UserConfig wrapper specific actions */
getName: async () =>
callLibSessionWorker(['UserConfig', 'getName']) as Promise<
ReturnType<UserConfigWrapperActionsCalls['getName']>
>,
getProfilePicture: async () =>
callLibSessionWorker(['UserConfig', 'getProfilePicture']) as Promise<
ReturnType<UserConfigWrapperActionsCalls['getProfilePicture']>
>,
setName: async (name: string) =>
callLibSessionWorker(['UserConfig', 'setName', name]) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setName']>
>,
setProfilePicture: async (url: string, key: Uint8Array) =>
callLibSessionWorker(['UserConfig', 'setProfilePicture', url, key]) as Promise<
ReturnType<UserConfigWrapperActionsCalls['setProfilePicture']>
>,
};
export const ContactsWrapperActions: ContactsWrapperActionsCalls = {
/* Reuse the GenericWrapperActions with the ContactConfig argument */
init: async (ed25519Key: Uint8Array, dump: Uint8Array | null) =>
GenericWrapperActions.init('ContactsConfig', ed25519Key, dump),
confirmPushed: async (seqno: number) =>
GenericWrapperActions.confirmPushed('ContactsConfig', seqno),
dump: async () => GenericWrapperActions.dump('ContactsConfig'),
merge: async (toMerge: Array<Uint8Array>) =>
GenericWrapperActions.merge('ContactsConfig', toMerge),
needsDump: async () => GenericWrapperActions.needsDump('ContactsConfig'),
needsPush: async () => GenericWrapperActions.needsPush('ContactsConfig'),
push: async () => GenericWrapperActions.push('ContactsConfig'),
storageNamespace: async () => GenericWrapperActions.storageNamespace('ContactsConfig'),
/** ContactsConfig wrapper specific actions */
get: async (pubkeyHex: string) =>
callLibSessionWorker(['ContactsConfig', 'get', pubkeyHex]) as Promise<
ReturnType<ContactsWrapperActionsCalls['get']>
>,
getOrCreate: async (pubkeyHex: string) =>
callLibSessionWorker(['ContactsConfig', 'getOrCreate', pubkeyHex]) as Promise<
ReturnType<ContactsWrapperActionsCalls['getOrCreate']>
>,
getAll: async () =>
callLibSessionWorker(['ContactsConfig', 'getAll']) as Promise<
ReturnType<ContactsWrapperActionsCalls['getAll']>
>,
erase: async (pubkeyHex: string) =>
callLibSessionWorker(['ContactsConfig', 'erase', pubkeyHex]) as Promise<
ReturnType<ContactsWrapperActionsCalls['erase']>
>,
set: async (contact: ContactInfo) =>
callLibSessionWorker(['ContactsConfig', 'set', contact]) as Promise<
ReturnType<ContactsWrapperActionsCalls['set']>
>,
setApproved: async (pubkeyHex: string, approved: boolean) =>
callLibSessionWorker(['ContactsConfig', 'setApproved', pubkeyHex, approved]) as Promise<
ReturnType<ContactsWrapperActionsCalls['setApproved']>
>,
setApprovedMe: async (pubkeyHex: string, approvedMe: boolean) =>
callLibSessionWorker(['ContactsConfig', 'setApprovedMe', pubkeyHex, approvedMe]) as Promise<
ReturnType<ContactsWrapperActionsCalls['setApprovedMe']>
>,
setBlocked: async (pubkeyHex: string, blocked: boolean) =>
callLibSessionWorker(['ContactsConfig', 'setBlocked', pubkeyHex, blocked]) as Promise<
ReturnType<ContactsWrapperActionsCalls['setBlocked']>
>,
setName: async (pubkeyHex: string, name: string) =>
callLibSessionWorker(['ContactsConfig', 'setName', pubkeyHex, name]) as Promise<
ReturnType<ContactsWrapperActionsCalls['setName']>
>,
setNickname: async (pubkeyHex: string, nickname: string) =>
callLibSessionWorker(['ContactsConfig', 'setNickname', pubkeyHex, nickname]) as Promise<
ReturnType<ContactsWrapperActionsCalls['setNickname']>
>,
setProfilePicture: async (pubkeyHex: string, url: string, key: Uint8Array) =>
callLibSessionWorker(['ContactsConfig', 'setProfilePicture', pubkeyHex, url, key]) as Promise<
ReturnType<ContactsWrapperActionsCalls['setProfilePicture']>
>,
};
const callLibSessionWorker = async (callToMake: LibSessionWorkerFunctions): Promise<unknown> => {
return internalCallLibSessionWorker(callToMake); return internalCallLibSessionWorker(callToMake);
}; };

@ -1,19 +1,22 @@
import _, { isEmpty, isNull } from 'lodash'; import _, { isEmpty, isNull } from 'lodash';
import { BaseConfigWrapper, ContactsConfigWrapper, UserConfigWrapper } from 'session_util_wrapper'; import {
BaseConfigWrapper,
BaseConfigWrapperInsideWorker,
ContactsConfigWrapperInsideWorker,
UserConfigWrapperInsideWorker,
} from 'session_util_wrapper';
import { ConfigWrapperObjectTypes } from '../../browser/libsession_worker_functions'; import { ConfigWrapperObjectTypes } from '../../browser/libsession_worker_functions';
/* eslint-disable no-console */ /* eslint-disable no-console */
/* eslint-disable strict */ /* eslint-disable strict */
// we can only have one of those so don't worry about storing them in a map for now // we can only have one of those so don't worry about storing them in a map for now
let userProfileWrapper: UserConfigWrapper | undefined; let userProfileWrapper: UserConfigWrapperInsideWorker | undefined;
let contactsConfigWrapper: ContactsConfigWrapper | undefined; let contactsConfigWrapper: ContactsConfigWrapperInsideWorker | undefined;
// const configWrappers: Array<EntryUserConfig | EntryContactsConfig> = new Array();
type UserWrapperType = 'UserConfig' | 'ContactsConfig'; type UserWrapperType = 'UserConfig' | 'ContactsConfig';
function getUserWrapper(type: UserWrapperType): BaseConfigWrapper | undefined { function getUserWrapper(type: UserWrapperType): BaseConfigWrapperInsideWorker | undefined {
switch (type) { switch (type) {
case 'UserConfig': case 'UserConfig':
return userProfileWrapper; return userProfileWrapper;
@ -22,7 +25,9 @@ function getUserWrapper(type: UserWrapperType): BaseConfigWrapper | undefined {
} }
} }
function getCorrespondingWrapper(wrapperType: ConfigWrapperObjectTypes): BaseConfigWrapper { function getCorrespondingWrapper(
wrapperType: ConfigWrapperObjectTypes
): BaseConfigWrapperInsideWorker {
switch (wrapperType) { switch (wrapperType) {
case 'UserConfig': case 'UserConfig':
case 'ContactsConfig': case 'ContactsConfig':
@ -68,10 +73,10 @@ function initUserWrapper(options: Array<any>, wrapperType: UserWrapperType): Bas
const userType = assertUserWrapperType(wrapperType); const userType = assertUserWrapperType(wrapperType);
switch (userType) { switch (userType) {
case 'UserConfig': case 'UserConfig':
userProfileWrapper = new UserConfigWrapper(edSecretKey, dump); userProfileWrapper = new UserConfigWrapperInsideWorker(edSecretKey, dump);
return userProfileWrapper; return userProfileWrapper;
case 'ContactsConfig': case 'ContactsConfig':
contactsConfigWrapper = new ContactsConfigWrapper(edSecretKey, dump); contactsConfigWrapper = new ContactsConfigWrapperInsideWorker(edSecretKey, dump);
return contactsConfigWrapper; return contactsConfigWrapper;
} }
} }

Loading…
Cancel
Save