From 8a0074d2bdcef7cd47dfd766ea8672ba74b6eb45 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 29 Mar 2023 14:30:35 +1100 Subject: [PATCH] feat: add convo volatile info during migrations for each convo tracked --- ts/models/conversation.ts | 40 ++- ts/node/migration/sessionMigrations.ts | 260 +++++++++++++----- ts/node/sql.ts | 5 + .../conversations/ConversationController.ts | 16 +- .../libsession_utils_convo_info_volatile.ts | 5 +- ts/session/utils/sync/syncUtils.ts | 1 - 6 files changed, 217 insertions(+), 110 deletions(-) diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index a855c0b6c..e63ecf58a 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -1531,7 +1531,7 @@ export class ConversationModel extends Backbone.Model { ReduxSogsRoomInfos.setCanWriteOutsideRedux(this.id, !!write); } - const adminChanged = await this.handleModsOrAdminsChanges({ + const adminChanged = await this.handleSogsModsOrAdminsChanges({ modsOrAdmins: details.admins, hiddenModsOrAdmins: details.hidden_admins, type: 'admins', @@ -1539,7 +1539,7 @@ export class ConversationModel extends Backbone.Model { hasChange = hasChange || adminChanged; - const modsChanged = await this.handleModsOrAdminsChanges({ + const modsChanged = await this.handleSogsModsOrAdminsChanges({ modsOrAdmins: details.moderators, hiddenModsOrAdmins: details.hidden_moderators, type: 'mods', @@ -2045,33 +2045,24 @@ export class ConversationModel extends Backbone.Model { } private sendTypingMessage(isTyping: boolean) { - if (!this.isPrivate()) { + // we can only send typing messages to approved contacts + if (!this.isPrivate() || this.isMe() || !this.isApproved()) { return; } - const recipientId = this.id; + const recipientId = this.id as string; - if (!recipientId) { + if (isEmpty(recipientId)) { throw new Error('Need to provide either recipientId'); } - if (!this.isApproved()) { - return; - } - - if (this.isMe()) { - // note to self - return; - } - const typingParams = { - timestamp: Date.now(), + timestamp: GetNetworkTime.getNowWithNetworkOffset(), isTyping, - typingTimestamp: Date.now(), + typingTimestamp: GetNetworkTime.getNowWithNetworkOffset(), }; const typingMessage = new TypingMessage(typingParams); - // send the message to a single recipient if this is a session chat const device = new PubKey(recipientId); getMessageQueue() .sendToPubKey(device, typingMessage, SnodeNamespaces.UserMessages) @@ -2091,7 +2082,7 @@ export class ConversationModel extends Backbone.Model { return replacedWithOurRealSessionId; } - private async handleModsOrAdminsChanges({ + private async handleSogsModsOrAdminsChanges({ modsOrAdmins, hiddenModsOrAdmins, type, @@ -2110,12 +2101,15 @@ export class ConversationModel extends Backbone.Model { uniq(localModsOrAdmins) ); - if (type === 'admins') { - return await this.updateGroupAdmins(replacedWithOurRealSessionId, false); + switch (type) { + case 'admins': + return this.updateGroupAdmins(replacedWithOurRealSessionId, false); + case 'mods': + ReduxSogsRoomInfos.setModeratorsOutsideRedux(this.id, replacedWithOurRealSessionId); + return false; + default: + assertUnreachable(type, `handleSogsModsOrAdminsChanges: unhandled switch case: ${type}`); } - - ReduxSogsRoomInfos.setModeratorsOutsideRedux(this.id, replacedWithOurRealSessionId); - return false; } return false; } diff --git a/ts/node/migration/sessionMigrations.ts b/ts/node/migration/sessionMigrations.ts index 6a9ae8fcb..74605cad5 100644 --- a/ts/node/migration/sessionMigrations.ts +++ b/ts/node/migration/sessionMigrations.ts @@ -1,7 +1,8 @@ import * as BetterSqlite3 from 'better-sqlite3'; -import { compact, isArray, isEmpty, isString, map, pick } from 'lodash'; +import { compact, isArray, isEmpty, isNumber, isString, map, pick } from 'lodash'; import { ContactsConfigWrapperInsideWorker, + ConvoInfoVolatileWrapperInsideWorker, UserConfigWrapperInsideWorker, UserGroupsWrapperInsideWorker, } from 'session_util_wrapper'; @@ -19,7 +20,6 @@ import { CONVERSATIONS_TABLE, dropFtsAndTriggers, GUARD_NODE_TABLE, - jsonToObject, LAST_HASHES_TABLE, MESSAGES_TABLE, NODES_FOR_PUBKEY_TABLE, @@ -1205,66 +1205,96 @@ function updateToSessionSchemaVersion29(currentVersion: number, db: BetterSqlite console.log(`updateToSessionSchemaVersion${targetVersion}: success!`); } -function insertContactIntoWrapper( +function insertContactIntoContactWrapper( contact: any, blockedNumbers: Array, - contactsConfigWrapper: ContactsConfigWrapperInsideWorker + contactsConfigWrapper: ContactsConfigWrapperInsideWorker | null, // set this to null to only insert into the convo volatile wrapper (i.e. for ourConvo case) + volatileConfigWrapper: ConvoInfoVolatileWrapperInsideWorker, + db: BetterSqlite3.Database ) { - const dbApproved = !!contact.isApproved || false; - const dbApprovedMe = !!contact.didApproveMe || false; - const dbBlocked = blockedNumbers.includes(contact.id); - const hidden = contact.hidden || false; - const isPinned = contact.isPinned; - - const wrapperContact = getContactInfoFromDBValues({ - id: contact.id, - dbApproved, - dbApprovedMe, - dbBlocked, - dbName: contact.displayNameInProfile || undefined, - dbNickname: contact.nickname || undefined, - dbProfileKey: contact.profileKey || undefined, - dbProfileUrl: contact.avatarPointer || undefined, - isPinned, - hidden, - }); + if (contactsConfigWrapper !== null) { + const dbApproved = !!contact.isApproved || false; + const dbApprovedMe = !!contact.didApproveMe || false; + const dbBlocked = blockedNumbers.includes(contact.id); + const hidden = contact.hidden || false; + const isPinned = contact.isPinned; + + const wrapperContact = getContactInfoFromDBValues({ + id: contact.id, + dbApproved, + dbApprovedMe, + dbBlocked, + dbName: contact.displayNameInProfile || undefined, + dbNickname: contact.nickname || undefined, + dbProfileKey: contact.profileKey || undefined, + dbProfileUrl: contact.avatarPointer || undefined, + isPinned, + hidden, + }); - try { - console.info('Inserting contact into wrapper: ', wrapperContact); - contactsConfigWrapper.set(wrapperContact); - } catch (e) { - console.error( - `contactsConfigWrapper.set during migration failed with ${e.message} for id: ${contact.id}` - ); - // the wrapper did not like something. Try again with just the boolean fields as it's most likely the issue is with one of the strings (which could be recovered) try { - console.info('Inserting edited contact into wrapper: ', contact.id); - contactsConfigWrapper.set( - getContactInfoFromDBValues({ - id: contact.id, - dbApproved, - dbApprovedMe, - dbBlocked, - dbName: undefined, - dbNickname: undefined, - dbProfileKey: undefined, - dbProfileUrl: undefined, - isPinned: false, - hidden, - }) - ); + console.info('Inserting contact into wrapper: ', wrapperContact); + contactsConfigWrapper.set(wrapperContact); } catch (e) { - // there is nothing else we can do here console.error( - `contactsConfigWrapper.set during migration failed with ${e.message} for id: ${contact.id}. Skipping contact entirely` + `contactsConfigWrapper.set during migration failed with ${e.message} for id: ${contact.id}` ); + // the wrapper did not like something. Try again with just the boolean fields as it's most likely the issue is with one of the strings (which could be recovered) + try { + console.info('Inserting edited contact into wrapper: ', contact.id); + contactsConfigWrapper.set( + getContactInfoFromDBValues({ + id: contact.id, + dbApproved, + dbApprovedMe, + dbBlocked, + dbName: undefined, + dbNickname: undefined, + dbProfileKey: undefined, + dbProfileUrl: undefined, + isPinned: false, + hidden, + }) + ); + } catch (e) { + // there is nothing else we can do here + console.error( + `contactsConfigWrapper.set during migration failed with ${e.message} for id: ${contact.id}. Skipping contact entirely` + ); + } } } + + try { + const rows = db + .prepare( + ` + SELECT MAX(COALESCE(sent_at, 0)) AS max_sent_at + FROM ${MESSAGES_TABLE} WHERE + conversationId = $conversationId AND + unread = $unread; + ` + ) + .get({ + conversationId: contact.id, + unread: toSqliteBoolean(false), // we want to find the message read with the higher sentAt timestamp + }); + + const maxRead = rows?.max_sent_at; + const lastRead = isNumber(maxRead) && isFinite(maxRead) ? maxRead : 0; + console.info(`Inserting contact into volatile wrapper maxread: ${contact.id} :${lastRead}`); + volatileConfigWrapper.set1o1(contact.id, lastRead, false); + } catch (e) { + console.error( + `volatileConfigWrapper.set1o1 during migration failed with ${e.message} for id: ${contact.id}. skipping` + ); + } } function insertCommunityIntoWrapper( community: { id: string; isPinned: boolean }, userGroupConfigWrapper: UserGroupsWrapperInsideWorker, + volatileConfigWrapper: ConvoInfoVolatileWrapperInsideWorker, db: BetterSqlite3.Database ) { const isPinned = community.isPinned; @@ -1301,8 +1331,26 @@ function insertCommunityIntoWrapper( }); try { - console.info('Inserting community into wrapper: ', wrapperComm); + console.info('Inserting community into group wrapper: ', wrapperComm); userGroupConfigWrapper.setCommunityByFullUrl(wrapperComm.fullUrl, wrapperComm.priority); + const rows = db + .prepare( + ` + SELECT MAX(COALESCE(serverTimestamp, 0)) AS max_sent_at + FROM ${MESSAGES_TABLE} WHERE + conversationId = $conversationId AND + unread = $unread; + ` + ) + .get({ + conversationId: convoId, + unread: toSqliteBoolean(false), // we want to find the message read with the higher serverTimestamp timestamp + }); + + const maxRead = rows?.max_sent_at; + const lastRead = isNumber(maxRead) && isFinite(maxRead) ? maxRead : 0; + console.info(`Inserting community into volatile wrapper: ${wrapperComm.fullUrl} :${lastRead}`); + volatileConfigWrapper.setCommunityByFullUrl(wrapperComm.fullUrl, lastRead, false); } catch (e) { console.error( `userGroupConfigWrapper.set during migration failed with ${e.message} for fullUrl: "${wrapperComm.fullUrl}". Skipping community entirely` @@ -1316,6 +1364,7 @@ function insertLegacyGroupIntoWrapper( 'hidden' | 'id' | 'isPinned' | 'expireTimer' | 'displayNameInProfile' > & { members: string; groupAdmins: string }, // members and groupAdmins are still stringified here userGroupConfigWrapper: UserGroupsWrapperInsideWorker, + volatileInfoConfigWrapper: ConvoInfoVolatileWrapperInsideWorker, db: BetterSqlite3.Database ) { const { @@ -1348,6 +1397,25 @@ function insertLegacyGroupIntoWrapper( try { console.info('Inserting legacy group into wrapper: ', wrapperLegacyGroup); userGroupConfigWrapper.setLegacyGroup(wrapperLegacyGroup); + + const rows = db + .prepare( + ` + SELECT MAX(COALESCE(sent_at, 0)) AS max_sent_at + FROM ${MESSAGES_TABLE} WHERE + conversationId = $conversationId AND + unread = $unread; + ` + ) + .get({ + conversationId: id, + unread: toSqliteBoolean(false), // we want to find the message read with the higher sentAt timestamp + }); + + const maxRead = rows?.max_sent_at; + const lastRead = isNumber(maxRead) && isFinite(maxRead) ? maxRead : 0; + console.info(`Inserting legacy group into volatile wrapper maxread: ${id} :${lastRead}`); + volatileInfoConfigWrapper.setLegacyGroup(id, lastRead, false); } catch (e) { console.error( `userGroupConfigWrapper.set during migration failed with ${e.message} for legacyGroup.id: "${legacyGroup.id}". Skipping that legacy group entirely` @@ -1394,13 +1462,18 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite ); // drop unused readCapability & uploadCapability columns. Also move `writeCapability` to memory only value. db.exec(` - ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN readCapability; - ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN writeCapability; - ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN uploadCapability; - ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN subscriberCount; - ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN is_medium_group; - ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN groupModerators; + ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN readCapability; -- stored in a redux slice now + ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN writeCapability; -- stored in a redux slice now + ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN uploadCapability; -- stored in a redux slice now + ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN subscriberCount; -- stored in a redux slice now + ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN groupModerators; -- stored in a redux slice now + ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN is_medium_group; -- a medium group starts with 05 and has a type of group. We cache everything renderer side so there is no need for that field + `); + + // Didn't find any reference to this serverTimestamp in the unprocessed table needed, so let's clean it up + db.exec(` + ALTER TABLE unprocessed DROP COLUMN serverTimestamp; `); // mark every "active" private chats as not hidden db.prepare( @@ -1478,26 +1551,32 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite if (!userAlreadyCreated) { throw new Error('privateEd25519 was empty. Considering no users are logged in'); } + const blockedNumbers = getBlockedNumbersDuringMigration(db); + const { privateEd25519, publicKeyHex } = keys; const userProfileWrapper = new UserConfigWrapperInsideWorker(privateEd25519, null); const contactsConfigWrapper = new ContactsConfigWrapperInsideWorker(privateEd25519, null); const userGroupsConfigWrapper = new UserGroupsWrapperInsideWorker(privateEd25519, null); + const volatileInfoConfigWrapper = new ConvoInfoVolatileWrapperInsideWorker( + privateEd25519, + null + ); /** * Setup up the User profile wrapper with what is stored in our own conversation */ - const ourConvoRow = db.prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`).get({ - id: publicKeyHex, - }); + const ourConversation = db + .prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`) + .get({ + id: publicKeyHex, + }) as Record | undefined; - if (!ourConvoRow) { + if (!ourConversation) { throw new Error('Failed to find our logged in conversation while migrating'); } - const ourConversation = jsonToObject(ourConvoRow); - - // Insert the user profile into the userWrappoer + // Insert the user profile into the userWrapper const ourDbName = ourConversation.displayNameInProfile || ''; const ourDbProfileUrl = ourConversation.avatarPointer || ''; const ourDbProfileKey = fromHexToArray(ourConversation.profileKey || ''); @@ -1508,6 +1587,13 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite } else { userProfileWrapper.setProfilePicture('', new Uint8Array()); } + insertContactIntoContactWrapper( + ourConversation, + blockedNumbers, + null, + volatileInfoConfigWrapper, + db + ); // dump the user wrapper content and save it to the DB const userDump = userProfileWrapper.dump(); @@ -1531,7 +1617,6 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite /** * Setup up the Contacts Wrapper with all the contact details which needs to be stored in it. */ - const blockedNumbers = getBlockedNumbersDuringMigration(db); // this filter is based on the `isContactToStoreInContactsWrapper` function. const contactsToWriteInWrapper = db @@ -1553,7 +1638,13 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite ); contactsToWriteInWrapper.forEach(contact => { - insertContactIntoWrapper(contact, blockedNumbers, contactsConfigWrapper); + insertContactIntoContactWrapper( + contact, + blockedNumbers, + contactsConfigWrapper, + volatileInfoConfigWrapper, + db + ); }); console.info('===================== Done with contact inserting ======================='); @@ -1595,7 +1686,12 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite communitiesToWriteInWrapper.forEach(community => { try { console.info('Writing community: ', JSON.stringify(community)); - insertCommunityIntoWrapper(community, userGroupsConfigWrapper, db); + insertCommunityIntoWrapper( + community, + userGroupsConfigWrapper, + volatileInfoConfigWrapper, + db + ); } catch (e) { console.info(`failed to insert community with ${e.message}`, community); } @@ -1622,7 +1718,12 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite try { console.info('Writing legacy group: ', JSON.stringify(legacyGroup)); - insertLegacyGroupIntoWrapper(legacyGroup, userGroupsConfigWrapper, db); + insertLegacyGroupIntoWrapper( + legacyGroup, + userGroupsConfigWrapper, + volatileInfoConfigWrapper, + db + ); } catch (e) { console.info(`failed to insert legacy group with ${e.message}`, legacyGroup); } @@ -1632,7 +1733,6 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite '===================== Done with legacy group inserting =======================' ); } - // TODO we need to do the same for new groups once they are available const userGroupsDump = userGroupsConfigWrapper.dump(); @@ -1652,25 +1752,35 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite data: userGroupsDump, }); - // TODO add the conversation volatile one with handling of contacts and note to self and reference to `isConvoToStoreInWrapper` + const convoVolatileDump = volatileInfoConfigWrapper.dump(); + + db.prepare( + `INSERT OR REPLACE INTO ${CONFIG_DUMP_TABLE} ( + publicKey, + variant, + data + ) values ( + $publicKey, + $variant, + $data + );` + ).run({ + publicKey: publicKeyHex, + variant: 'ConvoInfoVolatileConfig', + data: convoVolatileDump, + }); // TODO we've just created the initial dumps. We have to add an initial SyncJob to the database so it is run on the next app start/ // or find another way of adding one on the next start (store an another item in the DB and check for it on app start?) + // or just start a conf sync job on app start } catch (e) { console.error(`failed to create initial wrapper: `, e.stack); + throw e; } - // db.exec(`ALTER TABLE conversations - // ADD COLUMN lastReadTimestampMs INTEGER; - // ; - // `); - // 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, so let's clean it up - db.exec(`ALTER TABLE unprocessed DROP COLUMN serverTimestamp;`); - writeSessionSchemaVersion(targetVersion, db); })(); diff --git a/ts/node/sql.ts b/ts/node/sql.ts index 5e95c1377..7270dc543 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -504,6 +504,11 @@ function fetchConvoMemoryDetails(convoId: string): SaveConversationReturn { const unreadCount = getUnreadCountByConversation(convoId); const lastReadTimestampMessageSentTimestamp = getLastMessageReadInConversation(convoId); + // TODO it would be nice to be able to remove the lastMessage and lastMessageStatus from the conversation table, and just return it when saving the conversation + // and saving it in memory only. + // But we'd need to update a bunch of things as we do some logic before setting the lastUpdate text and status mostly in `getMessagePropStatus` and `getNotificationText()` + // const lastMessages = getLastMessagesByConversation(convoId, 1) as Array:Record>; + return { mentionedUs: hasMentionedUsUnread, unreadCount, diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 903f12650..44d5dfa65 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -14,6 +14,7 @@ import { ConfigurationSync } from '../utils/job_runners/jobs/ConfigurationSyncJo import { SessionUtilContact } from '../utils/libsession/libsession_utils_contacts'; import { SessionUtilConvoInfoVolatile } from '../utils/libsession/libsession_utils_convo_info_volatile'; import { SessionUtilUserGroups } from '../utils/libsession/libsession_utils_user_groups'; +import { ConfigurationDumpSync } from '../utils/job_runners/jobs/ConfigurationSyncDumpJob'; let instance: ConversationController | null; @@ -207,12 +208,12 @@ export class ConversationController { await deleteAllMessagesByConvoIdNoConfirmation(id); window.log.info(`deleteContact messages destroyed: ${id}`); - // Closed/Medium group leaving + // Legacy group leaving if (conversation.isClosedGroup()) { window.log.info(`deleteContact ClosedGroup case: ${id}`); await leaveClosedGroup(conversation.id); await SessionUtilConvoInfoVolatile.removeLegacyGroupFromWrapper(conversation.id); - // open group v2 + await SessionUtilUserGroups.removeLegacyGroupFromWrapper(conversation.id); } else if (conversation.isPublic()) { window?.log?.info('leaving open group v2', conversation.id); // remove from the wrapper the entries before we remove the roomInfos, as we won't have the required community pubkey afterwards @@ -248,7 +249,7 @@ export class ConversationController { }); // we currently do not wish to reset the approved/approvedMe state when marking a private conversation as hidden // await conversation.setIsApproved(false, false); - await conversation.commit(); + await conversation.commit(); // this updates the wrappers content to reflect the hidden state // The note to self cannot be removed from the wrapper I suppose, as it must always be there // TODO I think we want to mark the contacts as hidden instead of removing them, so maybe keep the volatile info too? @@ -281,6 +282,7 @@ export class ConversationController { if (!fromSyncMessage) { await ConfigurationSync.queueNewJobIfNeeded(); + await ConfigurationDumpSync.queueNewJobIfNeeded(); } } @@ -294,10 +296,6 @@ export class ConversationController { return this.conversations.models; } - public unsafeDelete(convo: ConversationModel) { - this.conversations.remove(convo); - } - public async load() { if (this.conversations.length) { throw new Error('ConversationController: Already loaded!'); @@ -311,7 +309,7 @@ export class ConversationController { const start = Date.now(); // TODO make this a switch so we take care of all wrappers and have compilation errors if we forgot to add one. - // also keep in mind that the convo volatile one need to run for each convo. + // also keep in mind that the convo volatile one need to run for each convo so it must be outside of a `else` for (let index = 0; index < convoModels.length; index++) { const convo = convoModels[index]; if (SessionUtilContact.isContactToStoreInContactsWrapper(convo)) { @@ -330,7 +328,7 @@ export class ConversationController { await convo.refreshInMemoryDetails(); } } - console.info(`refreshAllWrappersMappedValues took ${Date.now() - start}ms`); + window.log.info(`refreshAllWrappersMappedValues took ${Date.now() - start}ms`); this._initialFetchComplete = true; // TODO do we really need to do this? diff --git a/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts b/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts index ba3f8696b..c0fda689d 100644 --- a/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts +++ b/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts @@ -62,7 +62,7 @@ function isConvoToStoreInWrapper(convo: ConversationModel): boolean { return ( SessionUtilUserGroups.isUserGroupToStoreInWrapper(convo) || // this checks for community & legacy group SessionUtilContact.isContactToStoreInContactsWrapper(convo) || // this checks for contacts - SessionUtilUserProfile.isUserProfileToStoreInContactsWrapper(convo.id) // this checks for out own pubkey, as we want to keep track of the read state for the Note To Self + SessionUtilUserProfile.isUserProfileToStoreInContactsWrapper(convo.id) // this checks for our own pubkey, as we want to keep track of the read state for the Note To Self ); } @@ -91,7 +91,8 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise< const isForcedUnread = foundConvo.isMarkedUnread(); const timestampFromDbMs = (await Data.fetchConvoMemoryDetails(convoId))?.lastReadTimestampMessage; - // TODO not having a last read timestamp fallsback to 0, which keeps the existing value in the wrapper if it is already set (as done in src/convo_info_volatile_config.cpp) + // Note: not having a last read timestamp fallsback to 0, which keeps the existing value in the wrapper if it is already set (as done in src/convo_info_volatile_config.cpp) + // we actually do the max() of whatever is inside the wrapper and the value from the DB const lastReadMessageTimestamp = !!timestampFromDbMs && isFinite(timestampFromDbMs) && timestampFromDbMs > 0 ? timestampFromDbMs diff --git a/ts/session/utils/sync/syncUtils.ts b/ts/session/utils/sync/syncUtils.ts index 3d401e86e..2e663a6a4 100644 --- a/ts/session/utils/sync/syncUtils.ts +++ b/ts/session/utils/sync/syncUtils.ts @@ -95,7 +95,6 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal }); if (waitForMessageSent) { window.Whisper.events.once(ConfigurationSyncJobDone, () => { - debugger; resolve(true); }); }