diff --git a/ts/components/dialog/EditProfileDialog.tsx b/ts/components/dialog/EditProfileDialog.tsx index 7ff3a70be..1cccdc3c8 100644 --- a/ts/components/dialog/EditProfileDialog.tsx +++ b/ts/components/dialog/EditProfileDialog.tsx @@ -3,32 +3,25 @@ import { QRCode } from 'react-qr-svg'; import { Avatar, AvatarSize } from '../avatar/Avatar'; -import { YourSessionIDPill, YourSessionIDSelectable } from '../basic/YourSessionIDPill'; import { SyncUtils, ToastUtils, UserUtils } from '../../session/utils'; +import { YourSessionIDPill, YourSessionIDSelectable } from '../basic/YourSessionIDPill'; import { ConversationModel } from '../../models/conversation'; -import { getConversationController } from '../../session/conversations'; import autoBind from 'auto-bind'; -import { editProfileModal } from '../../state/ducks/modalDialog'; 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 { SessionSpinner } from '../basic/SessionSpinner'; import { SessionIconButton } from '../icon'; 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 { profileName: string; @@ -350,27 +343,8 @@ async function commitProfileEdits(newName: string, scaledAvatarUrl: string | nul await conversation.commit(); if (window.sessionFeatureFlags.useSharedUtilForUserConfig) { - await callLibSessionWorker(['UserConfig', 'setName', newName]); - const pointer = conversation.get('avatarPointer'); - 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 }); + await ConfigurationSync.queueNewJobIfNeeded(); + await setLastProfileUpdateTimestamp(Date.now()); } else { await setLastProfileUpdateTimestamp(Date.now()); diff --git a/ts/components/leftpane/ActionsPanel.tsx b/ts/components/leftpane/ActionsPanel.tsx index c03160023..5922ad299 100644 --- a/ts/components/leftpane/ActionsPanel.tsx +++ b/ts/components/leftpane/ActionsPanel.tsx @@ -44,7 +44,7 @@ import { LeftPaneSectionContainer } from './LeftPaneSectionContainer'; import { getLatestReleaseFromFileServer } from '../../session/apis/file_server_api/FileServerApi'; 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 { ThemeStateType } from '../../themes/constants/colors'; 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 */ const doAppStartUp = async () => { - await initializeLibSessionUtilWrappers(); + await LibSessionUtil.initializeLibSessionUtilWrappers(); void setupTheme(); // this generates the key to encrypt attachments locally @@ -201,6 +201,8 @@ const doAppStartUp = async () => { await runners.avatarDownloadRunner.loadJobsFromDb(); runners.avatarDownloadRunner.startProcessing(); + await runners.configurationSyncRunner.loadJobsFromDb(); + runners.configurationSyncRunner.startProcessing(); // trigger a sync message if needed for our other devices void triggerSyncIfNeeded(); diff --git a/ts/data/configDump/configDump.ts b/ts/data/configDump/configDump.ts index cd5ce1a06..098bbf9ec 100644 --- a/ts/data/configDump/configDump.ts +++ b/ts/data/configDump/configDump.ts @@ -1,41 +1,31 @@ import { - AsyncWrapper, + AsyncObjectWrapper, + ConfigDumpDataNode, ConfigDumpRow, - GetAllDumps, - GetByPubkeyConfigDump, - GetByVariantAndPubkeyConfigDump, - SaveConfigDump, + ConfigDumpRowWithoutData, } from '../../types/sqlSharedTypes'; import { ConfigWrapperObjectTypes } from '../../webworker/workers/browser/libsession_worker_functions'; import { channels } from '../channels'; -const getByVariantAndPubkey: AsyncWrapper = ( - variant: ConfigWrapperObjectTypes, - pubkey: string -) => { - return channels.getConfigDumpByVariantAndPubkey(variant, pubkey); -}; - -const getByPubkey: AsyncWrapper = (pubkey: string) => { - return channels.getConfigDumpsByPk(pubkey); -}; - -const saveConfigDump: AsyncWrapper = (dump: ConfigDumpRow) => { - return channels.saveConfigDump(dump); -}; - -const getAllDumpsWithData: AsyncWrapper = () => { - return channels.getAllDumpsWithData(); -}; - -const getAllDumpsWithoutData: AsyncWrapper = () => { - return channels.getAllDumpsWithoutData(); -}; - -export const ConfigDumpData = { - getByVariantAndPubkey, - getByPubkey, - saveConfigDump, - getAllDumpsWithData, - getAllDumpsWithoutData, +export const ConfigDumpData: AsyncObjectWrapper = { + getByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, pubkey: string) => { + return channels.getByVariantAndPubkey(variant, pubkey); + }, + getMessageHashesByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, pubkey: string) => { + return channels.getMessageHashesByVariantAndPubkey(variant, pubkey); + }, + saveConfigDump: (dump: ConfigDumpRow) => { + console.warn('saveConfigDump', dump); + return channels.saveConfigDump(dump); + }, + saveCombinedMessageHashesForMatching: (dump: ConfigDumpRowWithoutData) => { + console.warn('saveCombinedMessageHashesForMatching', dump); + return channels.saveCombinedMessageHashesForMatching(dump); + }, + getAllDumpsWithData: () => { + return channels.getAllDumpsWithData(); + }, + getAllDumpsWithoutData: () => { + return channels.getAllDumpsWithoutData(); + }, }; diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 3cdf64033..47c64a070 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -40,6 +40,7 @@ import { setLastProfileUpdateTimestamp } from '../util/storage'; import { getSodiumRenderer } from '../session/crypto'; import { encryptProfile } from '../util/crypto/profileEncrypter'; import { uploadFileToFsWithOnionV4 } from '../session/apis/file_server_api/FileServerApi'; +import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob'; export const getCompleteUrlForV2ConvoId = async (convoId: string) => { if (convoId.match(openGroupV2ConversationIdRegex)) { @@ -449,7 +450,11 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) { if (newAvatarDecrypted) { await setLastProfileUpdateTimestamp(Date.now()); - await SyncUtils.forceSyncConfigurationNowIfNeeded(true); + if (window.sessionFeatureFlags.useSharedUtilForUserConfig) { + await ConfigurationSync.queueNewJobIfNeeded(); + } else { + await SyncUtils.forceSyncConfigurationNowIfNeeded(true); + } } else { window.log.info( `Reuploading avatar finished at ${newTimestampReupload}, newAttachmentPointer ${fileUrl}` diff --git a/ts/node/migration/sessionMigrations.ts b/ts/node/migration/sessionMigrations.ts index 36b9d44f4..2879db030 100644 --- a/ts/node/migration/sessionMigrations.ts +++ b/ts/node/migration/sessionMigrations.ts @@ -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;`); // we need to populate those fields with the current state of the conversation so let's throw null until this is done diff --git a/ts/node/sql.ts b/ts/node/sql.ts index 4e10e7bef..a17403f72 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -48,8 +48,6 @@ import { } from './database_utility'; import { - ConfigDumpDataNode, - ConfigDumpRow, MsgDuplicateSearchOpenGroup, UnprocessedDataNode, UnprocessedParameter, @@ -70,7 +68,7 @@ import { initDbInstanceWith, isInstanceInitialized, } 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 @@ -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 */ diff --git a/ts/node/sql_calls/config_dump.ts b/ts/node/sql_calls/config_dump.ts new file mode 100644 index 000000000..c4c5a71d2 --- /dev/null +++ b/ts/node/sql_calls/config_dump.ts @@ -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 & CombinedMessageHashes +): ConfigDumpRow | null { + const parsedNoData = parseRowNoData(row); + if (!parsedNoData) { + return null; + } + return { ...parsedNoData, data: row.data }; +} + +function parseRowNoData( + row: Pick & CombinedMessageHashes +): ConfigDumpRowWithoutData | null { + const toRet: ConfigDumpRowWithoutData = { + publicKey: row.publicKey, + variant: row.variant, + combinedMessageHashes: [], + }; + toRet.combinedMessageHashes = parseRowMessageHashes(row); + + return toRet; +} + +function parseRowMessageHashes(row: CombinedMessageHashes): Array { + 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 || []), + }); + }, +}; diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 69f965454..d991f0f21 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -1,5 +1,5 @@ -import _, { isEmpty } from 'lodash'; -import { ContactInfo, ProfilePicture } from 'session_util_wrapper'; +import _, { compact, flattenDeep, isEmpty, isEqual } from 'lodash'; +import { ConfigDumpData } from '../data/configDump/configDump'; import { Data, hasSyncedInitialConfigurationItem } from '../data/data'; import { ConversationInteraction } from '../interactions'; import { ConversationTypeEnum } from '../models/conversationAttributes'; @@ -13,12 +13,17 @@ import { getConversationController } from '../session/conversations'; import { IncomingMessage } from '../session/messages/incoming/IncomingMessage'; import { ProfileManager } from '../session/profile_manager/ProfileManager'; import { UserUtils } from '../session/utils'; +import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob'; import { toHex } from '../session/utils/String'; import { configurationMessageReceived, trigger } from '../shims/events'; import { BlockedNumberController } from '../util'; import { getLastProfileUpdateTimestamp, setLastProfileUpdateTimestamp } from '../util/storage'; 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 { handleNewClosedGroup } from './closedGroups'; import { EnvelopePlus } from './types'; @@ -30,7 +35,7 @@ type IncomingConfResult = { latestSentTimestamp: number; }; -function protobufSharedConfigTypeToWrapper( +function pbKindToVariant( kind: SignalService.SharedConfigMessage.Kind ): ConfigWrapperObjectTypes | null { switch (kind) { @@ -45,59 +50,44 @@ function protobufSharedConfigTypeToWrapper( async function mergeConfigsWithIncomingUpdates( incomingConfig: IncomingMessage -) { - const kindMessageMap: Map = new Map(); - 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); +): Promise<{ kind: SignalService.SharedConfigMessage.Kind; result: IncomingConfResult }> { + const { kind } = incomingConfig.message; - const wrapperId = protobufSharedConfigTypeToWrapper(kind); - if (!wrapperId) { - throw new Error(`Invalid kind: ${kind}`); - } + const toMerge = [incomingConfig.message.data]; - await callLibSessionWorker([wrapperId, 'merge', toMerge]); - const needsPush = ((await callLibSessionWorker([wrapperId, 'needsPush'])) || false) as boolean; - const needsDump = ((await callLibSessionWorker([wrapperId, 'needsDump'])) || false) as boolean; - 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); + const wrapperId = pbKindToVariant(kind); + if (!wrapperId) { + throw new Error(`Invalid kind: ${kind}`); } - 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 { + const updatedUserName = await UserConfigWrapperActions.getName(); + console.warn('got', updatedUserName); + if (result.needsDump) { - return; + return result; } - const updatedUserName = (await callLibSessionWorker(['UserConfig', 'getName'])) as - | string - | undefined; - const updatedProfilePicture = (await callLibSessionWorker([ - 'UserConfig', - 'getProfilePicture', - ])) as ProfilePicture; - - // fetch our own conversation - const userPublicKey = UserUtils.getOurPubKeyStrFromCache(); - if (!userPublicKey) { - return; + if (!updatedUserName) { + debugger; } + const updatedProfilePicture = await UserConfigWrapperActions.getProfilePicture(); const picUpdate = !isEmpty(updatedProfilePicture.key) && !isEmpty(updatedProfilePicture.url); @@ -106,16 +96,15 @@ async function handleUserProfileUpdate(result: IncomingConfResult) { picUpdate ? updatedProfilePicture.url : null, picUpdate ? updatedProfilePicture.key : null ); + return result; } -async function handleContactsUpdate(result: IncomingConfResult) { +async function handleContactsUpdate(result: IncomingConfResult): Promise { if (result.needsDump) { - return; + return result; } - const allContacts = (await callLibSessionWorker(['ContactsConfig', 'getAll'])) as Array< - ContactInfo - >; + const allContacts = await ContactsWrapperActions.getAll(); for (let index = 0; index < allContacts.length; index++) { const wrapperConvo = allContacts[index]; @@ -161,7 +150,7 @@ async function handleContactsUpdate(result: IncomingConfResult) { 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( existingConvo.id, wrapperConvo.name, @@ -170,36 +159,85 @@ async function handleContactsUpdate(result: IncomingConfResult) { ); } } + return result; } async function processMergingResults( envelope: EnvelopePlus, - results: Map + 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 = keys[index]; - const result = results.get(kind); + const { kind, result: incomingResult } = result; - if (!result) { - continue; - } + if (!incomingResult) { + await removeFromCache(envelope); + return; + } - try { - switch (kind) { - case SignalService.SharedConfigMessage.Kind.USER_PROFILE: - await handleUserProfileUpdate(result); - break; - case SignalService.SharedConfigMessage.Kind.CONTACTS: - await handleContactsUpdate(result); - break; - } - } catch (e) { - throw e; + try { + let finalResult = incomingResult; + + switch (kind) { + case SignalService.SharedConfigMessage.Kind.USER_PROFILE: + finalResult = await handleUserProfileUpdate(incomingResult); + break; + case SignalService.SharedConfigMessage.Kind.CONTACTS: + finalResult = await handleContactsUpdate(incomingResult); + break; + 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); + // 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( @@ -218,11 +256,11 @@ async function handleConfigMessageViaLibSession( 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( diff --git a/ts/session/sending/MessageQueue.ts b/ts/session/sending/MessageQueue.ts index 6355e1cf1..527fd15de 100644 --- a/ts/session/sending/MessageQueue.ts +++ b/ts/session/sending/MessageQueue.ts @@ -231,7 +231,7 @@ export class MessageQueue { pubkey, }: { pubkey: PubKey; - message: ClosedGroupNewMessage | CallMessage; + message: ClosedGroupNewMessage | CallMessage | SharedConfigMessage; namespace: SnodeNamespaces; }): Promise { let rawMessage; @@ -258,6 +258,7 @@ export class MessageQueue { */ public async processPending(device: PubKey, isSyncMessage: boolean = false) { const messages = await this.pendingMessageCache.getForDevice(device); + console.warn('processPending', messages); const jobQueue = this.getJobQueue(device); messages.forEach(async message => { diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index 4f3fd2348..f6a28e138 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -124,7 +124,7 @@ export async function send( ? SnodeNamespaces.ClosedGroupMessage : SnodeNamespaces.UserMessages; } - let timestamp = networkTimestamp; + const timestamp = networkTimestamp; // the user config namespaces requires a signature to be added let signOpts: SnodeSignatureResult | undefined; if (SnodeNamespace.isUserConfigNamespace(namespace)) { @@ -247,7 +247,7 @@ export async function sendMessageToSnode({ window?.log?.info( `loki_message:::sendMessage - Successfully stored message to ${ed25519Str(pubKey)} via ${ snode.ip - }:${snode.port}` + }:${snode.port} on namespace: ${namespace}` ); } } diff --git a/ts/session/utils/job_runners/JobDeserialization.ts b/ts/session/utils/job_runners/JobDeserialization.ts index c7bb79049..4c70a9ac9 100644 --- a/ts/session/utils/job_runners/JobDeserialization.ts +++ b/ts/session/utils/job_runners/JobDeserialization.ts @@ -4,7 +4,7 @@ import { FakeSleepForMultiJob, } from '../../../test/session/unit/utils/job_runner/FakeSleepForJob'; import { AvatarDownload } from './jobs/AvatarDownloadJob'; -import { ConfigurationSyncJob } from './jobs/ConfigurationSyncJob'; +import { ConfigurationSync } from './jobs/ConfigurationSyncJob'; import { PersistedJob, TypeOfPersistedData } from './PersistedJob'; export function persistedJobFromData( @@ -16,7 +16,7 @@ export function persistedJobFromData( switch (data.jobType) { case 'ConfigurationSyncJobType': - return (new ConfigurationSyncJob(data) as unknown) as PersistedJob; + return (new ConfigurationSync.ConfigurationSyncJob(data) as unknown) as PersistedJob; case 'AvatarDownloadJobType': return (new AvatarDownload.AvatarDownloadJob(data) as unknown) as PersistedJob; diff --git a/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts b/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts index 0757db304..306c3b21e 100644 --- a/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts +++ b/ts/session/utils/job_runners/jobs/ConfigurationSyncJob.ts @@ -1,5 +1,17 @@ +import { from_string } from 'libsodium-wrappers-sumo'; +import { isNumber } from 'lodash'; +import Long from 'long'; 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 { AddJobCheckReturn, ConfigurationSyncPersistedData, @@ -8,36 +20,76 @@ import { } from '../PersistedJob'; const defaultMsBetweenRetries = 3000; +const defaultMaxAttempts = 3; -export class ConfigurationSyncJob extends PersistedJob { +class ConfigurationSyncJob extends PersistedJob { constructor({ identifier, nextAttemptTimestamp, maxAttempts, currentRetry, - }: Pick & - Partial>) { + }: Partial< + Pick< + ConfigurationSyncPersistedData, + 'identifier' | 'nextAttemptTimestamp' | 'currentRetry' | 'maxAttempts' + > + >) { super({ jobType: 'ConfigurationSyncJobType', identifier: identifier || v4(), delayBetweenRetries: defaultMsBetweenRetries, - maxAttempts: maxAttempts, + maxAttempts: isNumber(maxAttempts) ? maxAttempts : defaultMaxAttempts, + currentRetry: isNumber(currentRetry) ? currentRetry : 0, nextAttemptTimestamp: nextAttemptTimestamp || Date.now() + defaultMsBetweenRetries, - currentRetry, }); } public async run(): Promise { - // blablha do everything from the notion page, and if success, return true. - window.log.warn( - `running job ${this.persistedData.jobType} with id:"${this.persistedData.identifier}" ` - ); + window.log.debug(`ConfigurationSyncJob starting ${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); - window.log.warn( - `running job ${this.persistedData.jobType} with id:"${this.persistedData.identifier}" done and returning failed ` + const result = await getMessageQueue().sendToPubKeyNonDurably({ + message, + 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; } @@ -59,3 +111,16 @@ export class ConfigurationSyncJob extends PersistedJob = ['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(); - if (!keypair) { + if (!keypair || !keypair.privKeyBytes) { throw new Error('edkeypair not found for current user'); } 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(); for (let index = 0; index < dumps.length; index++) { const dump = dumps[index]; try { - await callLibSessionWorker([ + await GenericWrapperActions.init( dump.variant, - 'init', privateKeyEd25519, - dump.data.length ? dump.data : null, - ]); + dump.data.length ? dump.data : null + ); userVariantsBuildWithoutErrors.add(dump.variant); } catch (e) { @@ -31,14 +80,16 @@ export async function initializeLibSessionUtilWrappers() { } } - // TODO complete this list - const requiredVariants: Array = ['UserConfig', 'ContactsConfig']; // 'conversations' + console.warn('requiredVariants: FIXME add conversation volatile wrapper as required '); + const missingRequiredVariants: Array = difference(requiredVariants, [ ...userVariantsBuildWithoutErrors.values(), ]); for (let index = 0; index < missingRequiredVariants.length; index++) { const missingVariant = missingRequiredVariants[index]; - await callLibSessionWorker([missingVariant, 'init', privateKeyEd25519, null]); + await GenericWrapperActions.init(missingVariant, privateKeyEd25519, null); } } + +export const LibSessionUtil = { initializeLibSessionUtilWrappers }; diff --git a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_test_skip.ts b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_test_skip.ts index e01e92059..acbc3605c 100644 --- a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_test_skip.ts +++ b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_test_skip.ts @@ -35,7 +35,6 @@ describe('libsession_wrapper', () => { expect(pushResult.seqno).to.be.eq(0); expect(pushResult.data.length).to.be.eq(256); - expect(conf.encryptionDomain()).to.be.eq('UserProfile'); expect(conf.storageNamespace()).to.be.eq(2); expect(to_hex(pushResult.data)).to.be.deep.eq( '9ffb5347e061ac40d937ae4f1a890031475bdc11653f94c8ae1d516ffda71d9ee9cdaf9fbaeb15d835cdc7b3b6ecc120361f004ff172dd5e757c80ede10e88945536e6841255a7bca73664ab8a0607fcfe2579c05bb3d9d4b34ac1de2921e703783ce39e317a512cb9d4e3b59176cbde47b5ba24a03065bf8fefe3e8ca2609e0ad10c7c9c3f81dc6d3a399bda0c190e8a228d0acb22863ab84c2d0c411be74dac4de1f8bc18539635db01ea1ef7f28e505703d67786cb419690edd4bd8c92926fc1d6449eaccc31d7d9639e1b36222e5672b87d1e34b7860308c3f40b3997f39fecf6ceb889323826fa69e001816307799fc9fed302a90faa1e43f7cd7367c3c' diff --git a/ts/types/sqlSharedTypes.ts b/ts/types/sqlSharedTypes.ts index 54c7da67e..adf7d4430 100644 --- a/ts/types/sqlSharedTypes.ts +++ b/ts/types/sqlSharedTypes.ts @@ -9,6 +9,13 @@ export type AsyncWrapper any> = ( ...args: Parameters ) => Promise>; +/** + * 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 any>> = { + [Property in keyof Type]: AsyncWrapper; +}; + export type MsgDuplicateSearchOpenGroup = Array<{ sender: string; serverTimestamp: number; @@ -25,28 +32,33 @@ export type UpdateLastHashType = { export type ConfigDumpRow = { 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 - combinedMessageHashes?: string; // array of lastHashes to keep track of, stringified + combinedMessageHashes: Array; // array of lastHashes to keep track of // we might need to add a `seqno` field here. }; -// ========== configdump +export type ConfigDumpRowWithoutData = Pick< + ConfigDumpRow, + 'publicKey' | 'combinedMessageHashes' | 'variant' +>; -export type GetByVariantAndPubkeyConfigDump = ( - variant: ConfigWrapperObjectTypes, - pubkey: string -) => Array; -export type GetByPubkeyConfigDump = (pubkey: string) => Array; -export type SaveConfigDump = (dump: ConfigDumpRow) => void; -export type GetAllDumps = () => Array; +// ========== configdump export type ConfigDumpDataNode = { - getConfigDumpByVariantAndPubkey: GetByVariantAndPubkeyConfigDump; - getConfigDumpsByPubkey: GetByPubkeyConfigDump; - saveConfigDump: SaveConfigDump; - getAllDumpsWithData: GetAllDumps; - getAllDumpsWithoutData: GetAllDumps; + getByVariantAndPubkey: ( + variant: ConfigWrapperObjectTypes, + publicKey: string + ) => Array; + getMessageHashesByVariantAndPubkey: ( + variant: ConfigWrapperObjectTypes, + publicKey: string + ) => Array; + saveConfigDump: (dump: ConfigDumpRow) => void; + saveCombinedMessageHashesForMatching: (row: ConfigDumpRowWithoutData) => void; + + getAllDumpsWithData: () => Array; + getAllDumpsWithoutData: () => Array; }; // ========== unprocessed diff --git a/ts/webworker/workers/browser/libsession_worker_functions.d.ts b/ts/webworker/workers/browser/libsession_worker_functions.d.ts index 0ee7b79b1..81b18e5e0 100644 --- a/ts/webworker/workers/browser/libsession_worker_functions.d.ts +++ b/ts/webworker/workers/browser/libsession_worker_functions.d.ts @@ -1,6 +1,5 @@ import { BaseConfigActions, - BaseConfigWrapper, ContactsConfigActionsType, UserConfigActionsType, } from 'session_util_wrapper'; @@ -20,9 +19,4 @@ type ContactsConfigFunctions = | [ContactsConfig, ...BaseConfigActions] | [ContactsConfig, ...ContactsConfigActionsType]; -/**Those are the actions inherited from BaseConfigWrapper to ClosedGroupConfigWrapper */ -// type ClosedGroupConfigFromBase = [ClosedGroupConfig, ...BaseConfigActions]; -// type ClosedGroupConfigFunctions = ClosedGroupConfigFromBase; -//| ClosedGroupConfigFunctions; - export type LibSessionWorkerFunctions = UserConfigFunctions | ContactsConfigFunctions; diff --git a/ts/webworker/workers/browser/libsession_worker_interface.ts b/ts/webworker/workers/browser/libsession_worker_interface.ts index 0f40f676d..49b8c4304 100644 --- a/ts/webworker/workers/browser/libsession_worker_interface.ts +++ b/ts/webworker/workers/browser/libsession_worker_interface.ts @@ -1,7 +1,14 @@ import { WorkerInterface } from '../../worker_interface'; import { join } from 'path'; 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; @@ -26,8 +33,138 @@ const internalCallLibSessionWorker = async ([ return libsessionWorkerInterface?.callWorker(config, fnName, ...args); }; -export const callLibSessionWorker = async ( - callToMake: LibSessionWorkerFunctions -): Promise => { +export const GenericWrapperActions = { + init: async ( + wrapperId: ConfigWrapperObjectTypes, + ed25519Key: Uint8Array, + dump: Uint8Array | null + ) => + /** base wrapper generic actions */ + callLibSessionWorker([wrapperId, 'init', ed25519Key, dump]) as Promise, + confirmPushed: async (wrapperId: ConfigWrapperObjectTypes, seqno: number) => + callLibSessionWorker([wrapperId, 'confirmPushed', seqno]) as ReturnType< + BaseWrapperActionsCalls['confirmPushed'] + >, + dump: async (wrapperId: ConfigWrapperObjectTypes) => + callLibSessionWorker([wrapperId, 'dump']) as Promise< + ReturnType + >, + merge: async (wrapperId: ConfigWrapperObjectTypes, toMerge: Array) => + callLibSessionWorker([wrapperId, 'merge', toMerge]) as Promise< + ReturnType + >, + needsDump: async (wrapperId: ConfigWrapperObjectTypes) => + callLibSessionWorker([wrapperId, 'needsDump']) as Promise< + ReturnType + >, + needsPush: async (wrapperId: ConfigWrapperObjectTypes) => + callLibSessionWorker([wrapperId, 'needsPush']) as Promise< + ReturnType + >, + push: async (wrapperId: ConfigWrapperObjectTypes) => + callLibSessionWorker([wrapperId, 'push']) as Promise< + ReturnType + >, + storageNamespace: async (wrapperId: ConfigWrapperObjectTypes) => + callLibSessionWorker([wrapperId, 'storageNamespace']) as Promise< + ReturnType + >, +}; + +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) => 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 + >, + getProfilePicture: async () => + callLibSessionWorker(['UserConfig', 'getProfilePicture']) as Promise< + ReturnType + >, + setName: async (name: string) => + callLibSessionWorker(['UserConfig', 'setName', name]) as Promise< + ReturnType + >, + setProfilePicture: async (url: string, key: Uint8Array) => + callLibSessionWorker(['UserConfig', 'setProfilePicture', url, key]) as Promise< + ReturnType + >, +}; + +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) => + 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 + >, + getOrCreate: async (pubkeyHex: string) => + callLibSessionWorker(['ContactsConfig', 'getOrCreate', pubkeyHex]) as Promise< + ReturnType + >, + getAll: async () => + callLibSessionWorker(['ContactsConfig', 'getAll']) as Promise< + ReturnType + >, + + erase: async (pubkeyHex: string) => + callLibSessionWorker(['ContactsConfig', 'erase', pubkeyHex]) as Promise< + ReturnType + >, + + set: async (contact: ContactInfo) => + callLibSessionWorker(['ContactsConfig', 'set', contact]) as Promise< + ReturnType + >, + setApproved: async (pubkeyHex: string, approved: boolean) => + callLibSessionWorker(['ContactsConfig', 'setApproved', pubkeyHex, approved]) as Promise< + ReturnType + >, + setApprovedMe: async (pubkeyHex: string, approvedMe: boolean) => + callLibSessionWorker(['ContactsConfig', 'setApprovedMe', pubkeyHex, approvedMe]) as Promise< + ReturnType + >, + setBlocked: async (pubkeyHex: string, blocked: boolean) => + callLibSessionWorker(['ContactsConfig', 'setBlocked', pubkeyHex, blocked]) as Promise< + ReturnType + >, + setName: async (pubkeyHex: string, name: string) => + callLibSessionWorker(['ContactsConfig', 'setName', pubkeyHex, name]) as Promise< + ReturnType + >, + setNickname: async (pubkeyHex: string, nickname: string) => + callLibSessionWorker(['ContactsConfig', 'setNickname', pubkeyHex, nickname]) as Promise< + ReturnType + >, + setProfilePicture: async (pubkeyHex: string, url: string, key: Uint8Array) => + callLibSessionWorker(['ContactsConfig', 'setProfilePicture', pubkeyHex, url, key]) as Promise< + ReturnType + >, +}; + +const callLibSessionWorker = async (callToMake: LibSessionWorkerFunctions): Promise => { return internalCallLibSessionWorker(callToMake); }; diff --git a/ts/webworker/workers/node/libsession/libsession.worker.ts b/ts/webworker/workers/node/libsession/libsession.worker.ts index 0b31c9b42..51b8b82fa 100644 --- a/ts/webworker/workers/node/libsession/libsession.worker.ts +++ b/ts/webworker/workers/node/libsession/libsession.worker.ts @@ -1,19 +1,22 @@ 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'; /* eslint-disable no-console */ /* eslint-disable strict */ // we can only have one of those so don't worry about storing them in a map for now -let userProfileWrapper: UserConfigWrapper | undefined; -let contactsConfigWrapper: ContactsConfigWrapper | undefined; - -// const configWrappers: Array = new Array(); +let userProfileWrapper: UserConfigWrapperInsideWorker | undefined; +let contactsConfigWrapper: ContactsConfigWrapperInsideWorker | undefined; type UserWrapperType = 'UserConfig' | 'ContactsConfig'; -function getUserWrapper(type: UserWrapperType): BaseConfigWrapper | undefined { +function getUserWrapper(type: UserWrapperType): BaseConfigWrapperInsideWorker | undefined { switch (type) { case 'UserConfig': return userProfileWrapper; @@ -22,7 +25,9 @@ function getUserWrapper(type: UserWrapperType): BaseConfigWrapper | undefined { } } -function getCorrespondingWrapper(wrapperType: ConfigWrapperObjectTypes): BaseConfigWrapper { +function getCorrespondingWrapper( + wrapperType: ConfigWrapperObjectTypes +): BaseConfigWrapperInsideWorker { switch (wrapperType) { case 'UserConfig': case 'ContactsConfig': @@ -68,10 +73,10 @@ function initUserWrapper(options: Array, wrapperType: UserWrapperType): Bas const userType = assertUserWrapperType(wrapperType); switch (userType) { case 'UserConfig': - userProfileWrapper = new UserConfigWrapper(edSecretKey, dump); + userProfileWrapper = new UserConfigWrapperInsideWorker(edSecretKey, dump); return userProfileWrapper; case 'ContactsConfig': - contactsConfigWrapper = new ContactsConfigWrapper(edSecretKey, dump); + contactsConfigWrapper = new ContactsConfigWrapperInsideWorker(edSecretKey, dump); return contactsConfigWrapper; } }