feat: add convo volatile info during migrations for each convo tracked

pull/2620/head
Audric Ackermann 2 years ago
parent 88b5446c6e
commit 8a0074d2bd

@ -1531,7 +1531,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
ReduxSogsRoomInfos.setCanWriteOutsideRedux(this.id, !!write); ReduxSogsRoomInfos.setCanWriteOutsideRedux(this.id, !!write);
} }
const adminChanged = await this.handleModsOrAdminsChanges({ const adminChanged = await this.handleSogsModsOrAdminsChanges({
modsOrAdmins: details.admins, modsOrAdmins: details.admins,
hiddenModsOrAdmins: details.hidden_admins, hiddenModsOrAdmins: details.hidden_admins,
type: 'admins', type: 'admins',
@ -1539,7 +1539,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
hasChange = hasChange || adminChanged; hasChange = hasChange || adminChanged;
const modsChanged = await this.handleModsOrAdminsChanges({ const modsChanged = await this.handleSogsModsOrAdminsChanges({
modsOrAdmins: details.moderators, modsOrAdmins: details.moderators,
hiddenModsOrAdmins: details.hidden_moderators, hiddenModsOrAdmins: details.hidden_moderators,
type: 'mods', type: 'mods',
@ -2045,33 +2045,24 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
} }
private sendTypingMessage(isTyping: boolean) { private sendTypingMessage(isTyping: boolean) {
if (!this.isPrivate()) { // we can only send typing messages to approved contacts
if (!this.isPrivate() || this.isMe() || !this.isApproved()) {
return; return;
} }
const recipientId = this.id; const recipientId = this.id as string;
if (!recipientId) { if (isEmpty(recipientId)) {
throw new Error('Need to provide either recipientId'); throw new Error('Need to provide either recipientId');
} }
if (!this.isApproved()) {
return;
}
if (this.isMe()) {
// note to self
return;
}
const typingParams = { const typingParams = {
timestamp: Date.now(), timestamp: GetNetworkTime.getNowWithNetworkOffset(),
isTyping, isTyping,
typingTimestamp: Date.now(), typingTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
}; };
const typingMessage = new TypingMessage(typingParams); const typingMessage = new TypingMessage(typingParams);
// send the message to a single recipient if this is a session chat
const device = new PubKey(recipientId); const device = new PubKey(recipientId);
getMessageQueue() getMessageQueue()
.sendToPubKey(device, typingMessage, SnodeNamespaces.UserMessages) .sendToPubKey(device, typingMessage, SnodeNamespaces.UserMessages)
@ -2091,7 +2082,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
return replacedWithOurRealSessionId; return replacedWithOurRealSessionId;
} }
private async handleModsOrAdminsChanges({ private async handleSogsModsOrAdminsChanges({
modsOrAdmins, modsOrAdmins,
hiddenModsOrAdmins, hiddenModsOrAdmins,
type, type,
@ -2110,12 +2101,15 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
uniq(localModsOrAdmins) uniq(localModsOrAdmins)
); );
if (type === 'admins') { switch (type) {
return await this.updateGroupAdmins(replacedWithOurRealSessionId, false); 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; return false;
} }

@ -1,7 +1,8 @@
import * as BetterSqlite3 from 'better-sqlite3'; 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 { import {
ContactsConfigWrapperInsideWorker, ContactsConfigWrapperInsideWorker,
ConvoInfoVolatileWrapperInsideWorker,
UserConfigWrapperInsideWorker, UserConfigWrapperInsideWorker,
UserGroupsWrapperInsideWorker, UserGroupsWrapperInsideWorker,
} from 'session_util_wrapper'; } from 'session_util_wrapper';
@ -19,7 +20,6 @@ import {
CONVERSATIONS_TABLE, CONVERSATIONS_TABLE,
dropFtsAndTriggers, dropFtsAndTriggers,
GUARD_NODE_TABLE, GUARD_NODE_TABLE,
jsonToObject,
LAST_HASHES_TABLE, LAST_HASHES_TABLE,
MESSAGES_TABLE, MESSAGES_TABLE,
NODES_FOR_PUBKEY_TABLE, NODES_FOR_PUBKEY_TABLE,
@ -1205,66 +1205,96 @@ function updateToSessionSchemaVersion29(currentVersion: number, db: BetterSqlite
console.log(`updateToSessionSchemaVersion${targetVersion}: success!`); console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
} }
function insertContactIntoWrapper( function insertContactIntoContactWrapper(
contact: any, contact: any,
blockedNumbers: Array<string>, blockedNumbers: Array<string>,
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; if (contactsConfigWrapper !== null) {
const dbApprovedMe = !!contact.didApproveMe || false; const dbApproved = !!contact.isApproved || false;
const dbBlocked = blockedNumbers.includes(contact.id); const dbApprovedMe = !!contact.didApproveMe || false;
const hidden = contact.hidden || false; const dbBlocked = blockedNumbers.includes(contact.id);
const isPinned = contact.isPinned; const hidden = contact.hidden || false;
const isPinned = contact.isPinned;
const wrapperContact = getContactInfoFromDBValues({
id: contact.id, const wrapperContact = getContactInfoFromDBValues({
dbApproved, id: contact.id,
dbApprovedMe, dbApproved,
dbBlocked, dbApprovedMe,
dbName: contact.displayNameInProfile || undefined, dbBlocked,
dbNickname: contact.nickname || undefined, dbName: contact.displayNameInProfile || undefined,
dbProfileKey: contact.profileKey || undefined, dbNickname: contact.nickname || undefined,
dbProfileUrl: contact.avatarPointer || undefined, dbProfileKey: contact.profileKey || undefined,
isPinned, dbProfileUrl: contact.avatarPointer || undefined,
hidden, 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 { try {
console.info('Inserting edited contact into wrapper: ', contact.id); console.info('Inserting contact into wrapper: ', wrapperContact);
contactsConfigWrapper.set( contactsConfigWrapper.set(wrapperContact);
getContactInfoFromDBValues({
id: contact.id,
dbApproved,
dbApprovedMe,
dbBlocked,
dbName: undefined,
dbNickname: undefined,
dbProfileKey: undefined,
dbProfileUrl: undefined,
isPinned: false,
hidden,
})
);
} catch (e) { } catch (e) {
// there is nothing else we can do here
console.error( 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( function insertCommunityIntoWrapper(
community: { id: string; isPinned: boolean }, community: { id: string; isPinned: boolean },
userGroupConfigWrapper: UserGroupsWrapperInsideWorker, userGroupConfigWrapper: UserGroupsWrapperInsideWorker,
volatileConfigWrapper: ConvoInfoVolatileWrapperInsideWorker,
db: BetterSqlite3.Database db: BetterSqlite3.Database
) { ) {
const isPinned = community.isPinned; const isPinned = community.isPinned;
@ -1301,8 +1331,26 @@ function insertCommunityIntoWrapper(
}); });
try { try {
console.info('Inserting community into wrapper: ', wrapperComm); console.info('Inserting community into group wrapper: ', wrapperComm);
userGroupConfigWrapper.setCommunityByFullUrl(wrapperComm.fullUrl, wrapperComm.priority); 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) { } catch (e) {
console.error( console.error(
`userGroupConfigWrapper.set during migration failed with ${e.message} for fullUrl: "${wrapperComm.fullUrl}". Skipping community entirely` `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' 'hidden' | 'id' | 'isPinned' | 'expireTimer' | 'displayNameInProfile'
> & { members: string; groupAdmins: string }, // members and groupAdmins are still stringified here > & { members: string; groupAdmins: string }, // members and groupAdmins are still stringified here
userGroupConfigWrapper: UserGroupsWrapperInsideWorker, userGroupConfigWrapper: UserGroupsWrapperInsideWorker,
volatileInfoConfigWrapper: ConvoInfoVolatileWrapperInsideWorker,
db: BetterSqlite3.Database db: BetterSqlite3.Database
) { ) {
const { const {
@ -1348,6 +1397,25 @@ function insertLegacyGroupIntoWrapper(
try { try {
console.info('Inserting legacy group into wrapper: ', wrapperLegacyGroup); console.info('Inserting legacy group into wrapper: ', wrapperLegacyGroup);
userGroupConfigWrapper.setLegacyGroup(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) { } catch (e) {
console.error( console.error(
`userGroupConfigWrapper.set during migration failed with ${e.message} for legacyGroup.id: "${legacyGroup.id}". Skipping that legacy group entirely` `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. // drop unused readCapability & uploadCapability columns. Also move `writeCapability` to memory only value.
db.exec(` db.exec(`
ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN readCapability; ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN readCapability; -- stored in a redux slice now
ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN writeCapability; ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN writeCapability; -- stored in a redux slice now
ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN uploadCapability; ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN uploadCapability; -- stored in a redux slice now
ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN subscriberCount; ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN subscriberCount; -- stored in a redux slice now
ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN is_medium_group; ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN groupModerators; -- stored in a redux slice now
ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN groupModerators;
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 // mark every "active" private chats as not hidden
db.prepare( db.prepare(
@ -1478,26 +1551,32 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
if (!userAlreadyCreated) { if (!userAlreadyCreated) {
throw new Error('privateEd25519 was empty. Considering no users are logged in'); throw new Error('privateEd25519 was empty. Considering no users are logged in');
} }
const blockedNumbers = getBlockedNumbersDuringMigration(db);
const { privateEd25519, publicKeyHex } = keys; const { privateEd25519, publicKeyHex } = keys;
const userProfileWrapper = new UserConfigWrapperInsideWorker(privateEd25519, null); const userProfileWrapper = new UserConfigWrapperInsideWorker(privateEd25519, null);
const contactsConfigWrapper = new ContactsConfigWrapperInsideWorker(privateEd25519, null); const contactsConfigWrapper = new ContactsConfigWrapperInsideWorker(privateEd25519, null);
const userGroupsConfigWrapper = new UserGroupsWrapperInsideWorker(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 * 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({ const ourConversation = db
id: publicKeyHex, .prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`)
}); .get({
id: publicKeyHex,
}) as Record<string, any> | undefined;
if (!ourConvoRow) { if (!ourConversation) {
throw new Error('Failed to find our logged in conversation while migrating'); throw new Error('Failed to find our logged in conversation while migrating');
} }
const ourConversation = jsonToObject(ourConvoRow); // Insert the user profile into the userWrapper
// Insert the user profile into the userWrappoer
const ourDbName = ourConversation.displayNameInProfile || ''; const ourDbName = ourConversation.displayNameInProfile || '';
const ourDbProfileUrl = ourConversation.avatarPointer || ''; const ourDbProfileUrl = ourConversation.avatarPointer || '';
const ourDbProfileKey = fromHexToArray(ourConversation.profileKey || ''); const ourDbProfileKey = fromHexToArray(ourConversation.profileKey || '');
@ -1508,6 +1587,13 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
} else { } else {
userProfileWrapper.setProfilePicture('', new Uint8Array()); userProfileWrapper.setProfilePicture('', new Uint8Array());
} }
insertContactIntoContactWrapper(
ourConversation,
blockedNumbers,
null,
volatileInfoConfigWrapper,
db
);
// dump the user wrapper content and save it to the DB // dump the user wrapper content and save it to the DB
const userDump = userProfileWrapper.dump(); 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. * 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. // this filter is based on the `isContactToStoreInContactsWrapper` function.
const contactsToWriteInWrapper = db const contactsToWriteInWrapper = db
@ -1553,7 +1638,13 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
); );
contactsToWriteInWrapper.forEach(contact => { contactsToWriteInWrapper.forEach(contact => {
insertContactIntoWrapper(contact, blockedNumbers, contactsConfigWrapper); insertContactIntoContactWrapper(
contact,
blockedNumbers,
contactsConfigWrapper,
volatileInfoConfigWrapper,
db
);
}); });
console.info('===================== Done with contact inserting ======================='); console.info('===================== Done with contact inserting =======================');
@ -1595,7 +1686,12 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
communitiesToWriteInWrapper.forEach(community => { communitiesToWriteInWrapper.forEach(community => {
try { try {
console.info('Writing community: ', JSON.stringify(community)); console.info('Writing community: ', JSON.stringify(community));
insertCommunityIntoWrapper(community, userGroupsConfigWrapper, db); insertCommunityIntoWrapper(
community,
userGroupsConfigWrapper,
volatileInfoConfigWrapper,
db
);
} catch (e) { } catch (e) {
console.info(`failed to insert community with ${e.message}`, community); console.info(`failed to insert community with ${e.message}`, community);
} }
@ -1622,7 +1718,12 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
try { try {
console.info('Writing legacy group: ', JSON.stringify(legacyGroup)); console.info('Writing legacy group: ', JSON.stringify(legacyGroup));
insertLegacyGroupIntoWrapper(legacyGroup, userGroupsConfigWrapper, db); insertLegacyGroupIntoWrapper(
legacyGroup,
userGroupsConfigWrapper,
volatileInfoConfigWrapper,
db
);
} catch (e) { } catch (e) {
console.info(`failed to insert legacy group with ${e.message}`, legacyGroup); 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 =======================' '===================== Done with legacy group inserting ======================='
); );
} }
// TODO we need to do the same for new groups once they are available
const userGroupsDump = userGroupsConfigWrapper.dump(); const userGroupsDump = userGroupsConfigWrapper.dump();
@ -1652,25 +1752,35 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
data: userGroupsDump, 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/ // 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 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) { } catch (e) {
console.error(`failed to create initial wrapper: `, e.stack); 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" // for manually flagging conversations as :unread"
db.exec(`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN markedAsUnread BOOLEAN;`); 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); writeSessionSchemaVersion(targetVersion, db);
})(); })();

@ -504,6 +504,11 @@ function fetchConvoMemoryDetails(convoId: string): SaveConversationReturn {
const unreadCount = getUnreadCountByConversation(convoId); const unreadCount = getUnreadCountByConversation(convoId);
const lastReadTimestampMessageSentTimestamp = getLastMessageReadInConversation(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<string, any>>;
return { return {
mentionedUs: hasMentionedUsUnread, mentionedUs: hasMentionedUsUnread,
unreadCount, unreadCount,

@ -14,6 +14,7 @@ import { ConfigurationSync } from '../utils/job_runners/jobs/ConfigurationSyncJo
import { SessionUtilContact } from '../utils/libsession/libsession_utils_contacts'; import { SessionUtilContact } from '../utils/libsession/libsession_utils_contacts';
import { SessionUtilConvoInfoVolatile } from '../utils/libsession/libsession_utils_convo_info_volatile'; import { SessionUtilConvoInfoVolatile } from '../utils/libsession/libsession_utils_convo_info_volatile';
import { SessionUtilUserGroups } from '../utils/libsession/libsession_utils_user_groups'; import { SessionUtilUserGroups } from '../utils/libsession/libsession_utils_user_groups';
import { ConfigurationDumpSync } from '../utils/job_runners/jobs/ConfigurationSyncDumpJob';
let instance: ConversationController | null; let instance: ConversationController | null;
@ -207,12 +208,12 @@ export class ConversationController {
await deleteAllMessagesByConvoIdNoConfirmation(id); await deleteAllMessagesByConvoIdNoConfirmation(id);
window.log.info(`deleteContact messages destroyed: ${id}`); window.log.info(`deleteContact messages destroyed: ${id}`);
// Closed/Medium group leaving // Legacy group leaving
if (conversation.isClosedGroup()) { if (conversation.isClosedGroup()) {
window.log.info(`deleteContact ClosedGroup case: ${id}`); window.log.info(`deleteContact ClosedGroup case: ${id}`);
await leaveClosedGroup(conversation.id); await leaveClosedGroup(conversation.id);
await SessionUtilConvoInfoVolatile.removeLegacyGroupFromWrapper(conversation.id); await SessionUtilConvoInfoVolatile.removeLegacyGroupFromWrapper(conversation.id);
// open group v2 await SessionUtilUserGroups.removeLegacyGroupFromWrapper(conversation.id);
} else if (conversation.isPublic()) { } else if (conversation.isPublic()) {
window?.log?.info('leaving open group v2', conversation.id); 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 // 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 // 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.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 // 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? // 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) { if (!fromSyncMessage) {
await ConfigurationSync.queueNewJobIfNeeded(); await ConfigurationSync.queueNewJobIfNeeded();
await ConfigurationDumpSync.queueNewJobIfNeeded();
} }
} }
@ -294,10 +296,6 @@ export class ConversationController {
return this.conversations.models; return this.conversations.models;
} }
public unsafeDelete(convo: ConversationModel) {
this.conversations.remove(convo);
}
public async load() { public async load() {
if (this.conversations.length) { if (this.conversations.length) {
throw new Error('ConversationController: Already loaded!'); throw new Error('ConversationController: Already loaded!');
@ -311,7 +309,7 @@ export class ConversationController {
const start = Date.now(); 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. // 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++) { for (let index = 0; index < convoModels.length; index++) {
const convo = convoModels[index]; const convo = convoModels[index];
if (SessionUtilContact.isContactToStoreInContactsWrapper(convo)) { if (SessionUtilContact.isContactToStoreInContactsWrapper(convo)) {
@ -330,7 +328,7 @@ export class ConversationController {
await convo.refreshInMemoryDetails(); await convo.refreshInMemoryDetails();
} }
} }
console.info(`refreshAllWrappersMappedValues took ${Date.now() - start}ms`); window.log.info(`refreshAllWrappersMappedValues took ${Date.now() - start}ms`);
this._initialFetchComplete = true; this._initialFetchComplete = true;
// TODO do we really need to do this? // TODO do we really need to do this?

@ -62,7 +62,7 @@ function isConvoToStoreInWrapper(convo: ConversationModel): boolean {
return ( return (
SessionUtilUserGroups.isUserGroupToStoreInWrapper(convo) || // this checks for community & legacy group SessionUtilUserGroups.isUserGroupToStoreInWrapper(convo) || // this checks for community & legacy group
SessionUtilContact.isContactToStoreInContactsWrapper(convo) || // this checks for contacts 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 isForcedUnread = foundConvo.isMarkedUnread();
const timestampFromDbMs = (await Data.fetchConvoMemoryDetails(convoId))?.lastReadTimestampMessage; 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 = const lastReadMessageTimestamp =
!!timestampFromDbMs && isFinite(timestampFromDbMs) && timestampFromDbMs > 0 !!timestampFromDbMs && isFinite(timestampFromDbMs) && timestampFromDbMs > 0
? timestampFromDbMs ? timestampFromDbMs

@ -95,7 +95,6 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal
}); });
if (waitForMessageSent) { if (waitForMessageSent) {
window.Whisper.events.once(ConfigurationSyncJobDone, () => { window.Whisper.events.once(ConfigurationSyncJobDone, () => {
debugger;
resolve(true); resolve(true);
}); });
} }

Loading…
Cancel
Save