From f5efb52fea6519b201c8e00a335b61d35eb1e32e Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 10 Mar 2023 16:39:48 +1100 Subject: [PATCH] feat: remove closedgroupv3 uneeded stuff for now also move the room stuff which can be kept in memory into another redux slice --- ts/components/SessionInboxView.tsx | 5 + .../conversation/ConversationHeader.tsx | 41 +----- .../conversation/SessionRightPanel.tsx | 8 +- ts/models/conversation.ts | 70 +++------ ts/models/conversationAttributes.ts | 19 ++- ts/node/database_utility.ts | 29 +--- ts/node/migration/sessionMigrations.ts | 66 ++++----- ts/node/sql.ts | 40 +++--- ts/receiver/queuedJob.ts | 7 +- .../apis/open_group_api/sogsv3/sogsApiV3.ts | 2 +- ts/session/conversations/createClosedGroup.ts | 17 +-- ts/session/group/closed-group.ts | 24 +--- ts/state/ducks/conversations.ts | 4 - ts/state/ducks/index.ts | 29 ---- ts/state/ducks/sogsRoomInfo.tsx | 62 ++++++++ ts/state/ducks/userConfig.tsx | 9 +- ts/state/reducer.ts | 3 + ts/state/selectors/conversations.ts | 65 +++++---- ts/state/selectors/sogsRoomInfo.ts | 36 +++++ ts/state/selectors/userConfig.ts | 27 ++-- .../unit/models/ConversationModels_test.ts | 15 -- .../models/formatRowOfConversation_test.ts | 136 ++++++++++-------- .../unit/selectors/conversations_test.ts | 10 -- ts/util/accountManager.ts | 13 +- 24 files changed, 346 insertions(+), 391 deletions(-) delete mode 100644 ts/state/ducks/index.ts create mode 100644 ts/state/ducks/sogsRoomInfo.tsx create mode 100644 ts/state/selectors/sogsRoomInfo.ts diff --git a/ts/components/SessionInboxView.tsx b/ts/components/SessionInboxView.tsx index f4408d539..9feb12f6e 100644 --- a/ts/components/SessionInboxView.tsx +++ b/ts/components/SessionInboxView.tsx @@ -31,6 +31,7 @@ import { ExpirationTimerOptions } from '../util/expiringMessages'; // moment does not support es-419 correctly (and cause white screen on app start) import moment from 'moment'; import styled from 'styled-components'; +import { initialSogsRoomInfoState } from '../state/ducks/sogsRoomInfo'; // Default to the locale from env. It will be overriden if moment // does not recognize it with what moment knows which is the closest. @@ -117,11 +118,15 @@ export class SessionInboxView extends React.Component { }, stagedAttachments: getEmptyStagedAttachmentsState(), call: initialCallState, + sogsRoomInfo: initialSogsRoomInfoState, }; this.store = createStore(initialState); window.inboxStore = this.store; + console.warn('initialState', JSON.stringify(initialState)); + console.warn('this.store', JSON.stringify(this.store.getState())); + window.openConversationWithMessages = openConversationWithMessages; this.setState({ isInitialLoadComplete: true }); diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index a518a4e29..9404bf774 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -8,6 +8,7 @@ import { ConversationNotificationSettingType } from '../../models/conversationAt import { getConversationHeaderTitleProps, getCurrentNotificationSettingText, + getCurrentSubscriberCount, getIsSelectedActive, getIsSelectedBlocked, getIsSelectedNoteToSelf, @@ -55,37 +56,6 @@ export interface TimerOption { value: number; } -export type ConversationHeaderProps = { - conversationKey: string; - name?: string; - - profileName?: string; - avatarPath: string | null; - - isMe: boolean; - isGroup: boolean; - isPrivate: boolean; - isPublic: boolean; - weAreAdmin: boolean; - - // We might not always have the full list of members, - // e.g. for open groups where we could have thousands - // of members. We'll keep this for now (for closed chats) - members: Array; - - // not equal members.length (see above) - subscriberCount?: number; - - expirationSettingName?: string; - currentNotificationSetting: ConversationNotificationSettingType; - hasNickname: boolean; - - isBlocked: boolean; - - isKickedFromGroup: boolean; - left: boolean; -}; - const SelectionOverlay = () => { const selectedMessageIds = useSelector(getSelectedMessageIds); const selectedConversationKey = useSelector(getSelectedConversationKey); @@ -276,7 +246,6 @@ export type ConversationHeaderTitleProps = { isGroup: boolean; isPublic: boolean; members: Array; - subscriberCount?: number; isKickedFromGroup: boolean; currentNotificationSetting?: ConversationNotificationSettingType; }; @@ -295,17 +264,19 @@ export const ConversationHeaderSubtitle = (props: { text?: string | null }): JSX }; const ConversationHeaderTitle = () => { + const dispatch = useDispatch(); + const headerTitleProps = useSelector(getConversationHeaderTitleProps); const notificationSetting = useSelector(getCurrentNotificationSettingText); const isRightPanelOn = useSelector(isRightPanelShowing); - const convoName = useConversationUsername(headerTitleProps?.conversationKey); - const dispatch = useDispatch(); + const subscriberCount = useSelector(getCurrentSubscriberCount); + if (!headerTitleProps) { return null; } - const { isGroup, isPublic, members, subscriberCount, isMe, isKickedFromGroup } = headerTitleProps; + const { isGroup, isPublic, members, isMe, isKickedFromGroup } = headerTitleProps; const { i18n } = window; diff --git a/ts/components/conversation/SessionRightPanel.tsx b/ts/components/conversation/SessionRightPanel.tsx index 7a836dbb7..2927fbe89 100644 --- a/ts/components/conversation/SessionRightPanel.tsx +++ b/ts/components/conversation/SessionRightPanel.tsx @@ -17,7 +17,11 @@ import { } from '../../interactions/conversationInteractions'; import { Constants } from '../../session'; import { closeRightPanel } from '../../state/ducks/conversations'; -import { getSelectedConversation, isRightPanelShowing } from '../../state/selectors/conversations'; +import { + getCurrentSubscriberCount, + getSelectedConversation, + isRightPanelShowing, +} from '../../state/selectors/conversations'; import { getTimerOptions } from '../../state/selectors/timerOptions'; import { AttachmentTypeWithPath } from '../../types/Attachment'; import { Avatar, AvatarSize } from '../avatar/Avatar'; @@ -189,6 +193,7 @@ export const SessionRightPanelWithDetails = () => { const selectedConversation = useSelector(getSelectedConversation); const isShowing = useSelector(isRightPanelShowing); + const subscriberCount = useSelector(getCurrentSubscriberCount); useEffect(() => { let isRunning = true; @@ -229,7 +234,6 @@ export const SessionRightPanelWithDetails = () => { const { id, - subscriberCount, displayNameInProfile, isKickedFromGroup, left, diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index dbf529bb9..4e90f7f5a 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -80,6 +80,8 @@ import { ed25519Str } from '../session/onions/onionPath'; import { ConfigurationDumpSync } from '../session/utils/job_runners/jobs/ConfigurationSyncDumpJob'; import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob'; import { SessionUtilContact } from '../session/utils/libsession/libsession_utils_contacts'; +import { SessionUtilConvoInfoVolatile } from '../session/utils/libsession/libsession_utils_convo_info_volatile'; +import { SessionUtilUserGroups } from '../session/utils/libsession/libsession_utils_user_groups'; import { forceSyncConfigurationNowIfNeeded } from '../session/utils/sync/syncUtils'; import { getOurProfile } from '../session/utils/User'; import { createLastMessageUpdate } from '../types/Conversation'; @@ -90,8 +92,10 @@ import { } from '../types/MessageAttachment'; import { IMAGE_JPEG } from '../types/MIME'; import { Reaction } from '../types/Reaction'; +import { assertUnreachable } from '../types/sqlSharedTypes'; import { Notifications } from '../util/notifications'; import { Reactions } from '../util/reactions'; +import { Registration } from '../util/registration'; import { Storage } from '../util/storage'; import { ConversationAttributes, @@ -101,13 +105,14 @@ import { isDirectConversation, isOpenOrClosedGroup, } from './conversationAttributes'; -import { SessionUtilUserGroups } from '../session/utils/libsession/libsession_utils_user_groups'; -import { Registration } from '../util/registration'; -import { SessionUtilConvoInfoVolatile } from '../session/utils/libsession/libsession_utils_convo_info_volatile'; -import { assertUnreachable } from '../types/sqlSharedTypes'; import { LibSessionUtil } from '../session/utils/libsession/libsession_utils'; import { SessionUtilUserProfile } from '../session/utils/libsession/libsession_utils_user_profile'; +import { ReduxSogsRoomInfos } from '../state/ducks/sogsRoomInfo'; +import { + getCanWriteOutsideRedux, + getSubscriberCountOutsideRedux, +} from '../state/selectors/sogsRoomInfo'; export class ConversationModel extends Backbone.Model { public updateLastMessage: () => any; @@ -307,7 +312,6 @@ export class ConversationModel extends Backbone.Model { const isTyping = !!this.typingTimer; const unreadCount = this.get('unreadCount') || undefined; const mentionedUs = this.get('mentionedUs') || undefined; - const subscriberCount = this.get('subscriberCount'); const isKickedFromGroup = !!this.get('isKickedFromGroup'); const left = !!this.get('left'); const expireTimer = this.get('expireTimer'); @@ -435,10 +439,6 @@ export class ConversationModel extends Backbone.Model { toRet.left = left; } - if (subscriberCount) { - toRet.subscriberCount = subscriberCount; - } - if (groupAdmins && groupAdmins.length) { toRet.groupAdmins = uniq(groupAdmins); } @@ -471,16 +471,6 @@ export class ConversationModel extends Backbone.Model { if (room && isArray(room.capabilities) && room.capabilities.length) { toRet.capabilities = room.capabilities; } - - if (this.get('writeCapability')) { - toRet.writeCapability = this.get('writeCapability'); - } - if (this.get('readCapability')) { - toRet.readCapability = this.get('readCapability'); - } - if (this.get('uploadCapability')) { - toRet.uploadCapability = this.get('uploadCapability'); - } } const lastMessageText = this.get('lastMessage'); @@ -1375,13 +1365,10 @@ export class ConversationModel extends Backbone.Model { read = filter(read, m => Boolean(m.sender)); const realUnreadCount = await this.getUnreadCount(); if (read.length === 0) { - const cachedUnreadCountOnConvo = this.get('unreadCount'); - if (cachedUnreadCountOnConvo !== realUnreadCount) { + if (this.get('unreadCount') !== realUnreadCount) { // reset the unreadCount on the convo to the real one coming from markRead messages on the db this.set({ unreadCount: realUnreadCount }); await this.commit(); - } else { - // window?.log?.info('markRead(): nothing newly read.'); } return; } @@ -1624,21 +1611,13 @@ export class ConversationModel extends Backbone.Model { await this.commit(); } - public async setSubscriberCount(count: number) { - if (this.get('subscriberCount') !== count) { - this.set({ subscriberCount: count }); - await this.commit(); - } - // Not sure if we care about updating the database - } - /** * Saves the infos of that room directly on the conversation table. * This does not write anything to the db if no changes are detected */ // tslint:disable-next-line: cyclomatic-complexity public async setPollInfo(infos?: { - subscriberCount: number; + active_users: number; read: boolean; write: boolean; upload: boolean; @@ -1655,29 +1634,18 @@ export class ConversationModel extends Backbone.Model { return; } let hasChange = false; - const { read, write, upload, subscriberCount, details } = infos; + const { write, active_users, details } = infos; + if ( - isNumber(infos.subscriberCount) && - infos.subscriberCount !== 0 && - this.get('subscriberCount') !== subscriberCount + isFinite(infos.active_users) && + infos.active_users !== 0 && + getSubscriberCountOutsideRedux(this.id) !== active_users ) { - hasChange = true; - this.set('subscriberCount', subscriberCount); - } - - if (Boolean(this.get('readCapability')) !== Boolean(read)) { - hasChange = true; - this.set('readCapability', Boolean(read)); - } - - if (Boolean(this.get('writeCapability')) !== Boolean(write)) { - hasChange = true; - this.set('writeCapability', Boolean(write)); + ReduxSogsRoomInfos.setSubscriberCountOutsideRedux(this.id, active_users); } - if (Boolean(this.get('uploadCapability')) !== Boolean(upload)) { - hasChange = true; - this.set('uploadCapability', Boolean(upload)); + if (getCanWriteOutsideRedux(this.id) !== !!write) { + ReduxSogsRoomInfos.setCanWriteOutsideRedux(this.id, !!write); } const adminChanged = await this.handleModsOrAdminsChanges({ diff --git a/ts/models/conversationAttributes.ts b/ts/models/conversationAttributes.ts index aa04cd3ee..de1e4a4cc 100644 --- a/ts/models/conversationAttributes.ts +++ b/ts/models/conversationAttributes.ts @@ -36,6 +36,14 @@ export function isDirectConversation(conversationType: ConversationTypeEnum) { export const ConversationNotificationSetting = ['all', 'disabled', 'mentions_only'] as const; export type ConversationNotificationSettingType = typeof ConversationNotificationSetting[number]; +/** + * Soem fields are retrieved from the database as a select, but should not be saved in a commit() + */ +export type ConversationAttributesNotSaved = { + mentionedUs: boolean; + unreadCount: number; +}; + export interface ConversationAttributes { id: string; type: ConversationTypeEnum.PRIVATE | ConversationTypeEnum.GROUPV3 | ConversationTypeEnum.GROUP; @@ -68,11 +76,6 @@ export interface ConversationAttributes { groupModerators: Array; // for sogs only, this is the moderator in that room. isKickedFromGroup: boolean; - subscriberCount: number; - readCapability: boolean; - writeCapability: boolean; - uploadCapability: boolean; - is_medium_group: boolean; avatarPointer?: string; // this is the url of the avatar on the file server v2. we use this to detect if we need to redownload the avatar from someone (not used for opengroups) @@ -90,11 +93,6 @@ export interface ConversationAttributes { markedAsUnread: boolean; - /** - * When we create a closed group v3 or get promoted to admim, we need to save the private key of that closed group. - */ - // identityPrivateKey?: string; - hidden: boolean; } @@ -114,7 +112,6 @@ export const fillConvoAttributesWithDefaults = ( unreadCount: 0, lastJoinedTimestamp: 0, - subscriberCount: 0, expireTimer: 0, active_at: 0, diff --git a/ts/node/database_utility.ts b/ts/node/database_utility.ts index 0339029da..aff2ccb71 100644 --- a/ts/node/database_utility.ts +++ b/ts/node/database_utility.ts @@ -61,10 +61,6 @@ const allowedKeysFormatRowOfConversation = [ 'triggerNotificationsFor', 'unreadCount', 'lastJoinedTimestamp', - 'subscriberCount', - 'readCapability', - 'writeCapability', - 'uploadCapability', 'expireTimer', 'active_at', 'id', @@ -76,12 +72,14 @@ const allowedKeysFormatRowOfConversation = [ 'avatarInProfile', 'displayNameInProfile', 'conversationIdOrigin', - 'identityPrivateKey', 'markedAsUnread', 'hidden', ]; // tslint:disable: cyclomatic-complexity -export function formatRowOfConversation(row?: Record): ConversationAttributes | null { +export function formatRowOfConversation( + row: Record, + from: string +): ConversationAttributes | null { if (!row) { return null; } @@ -93,7 +91,7 @@ export function formatRowOfConversation(row?: Record): Conversation if (foundInRowButNotInAllowed?.length) { console.error( - 'formatRowOfConversation: foundInRowButNotInAllowed: ', + `formatRowOfConversation: "from:${from}" foundInRowButNotInAllowed: `, foundInRowButNotInAllowed ); @@ -131,9 +129,6 @@ export function formatRowOfConversation(row?: Record): Conversation convo.mentionedUs = Boolean(convo.mentionedUs); convo.isKickedFromGroup = Boolean(convo.isKickedFromGroup); convo.left = Boolean(convo.left); - convo.readCapability = Boolean(convo.readCapability); - convo.writeCapability = Boolean(convo.writeCapability); - convo.uploadCapability = Boolean(convo.uploadCapability); convo.markedAsUnread = Boolean(convo.markedAsUnread); convo.hidden = Boolean(convo.hidden); @@ -161,10 +156,6 @@ export function formatRowOfConversation(row?: Record): Conversation convo.lastJoinedTimestamp = 0; } - if (!convo.subscriberCount) { - convo.subscriberCount = 0; - } - if (!convo.expireTimer) { convo.expireTimer = 0; } @@ -173,11 +164,6 @@ export function formatRowOfConversation(row?: Record): Conversation convo.active_at = 0; } - // convo.identityPrivateKey = row.identityPrivateKey; - // if (!convo.identityPrivateKey) { - // convo.identityPrivateKey = undefined; - // } - return convo; } @@ -199,10 +185,6 @@ const allowedKeysOfConversationAttributes = [ 'triggerNotificationsFor', 'unreadCount', 'lastJoinedTimestamp', - 'subscriberCount', - 'readCapability', - 'writeCapability', - 'uploadCapability', 'expireTimer', 'active_at', 'id', @@ -214,7 +196,6 @@ const allowedKeysOfConversationAttributes = [ 'avatarInProfile', 'displayNameInProfile', 'conversationIdOrigin', - 'identityPrivateKey', 'markedAsUnread', 'hidden', ]; diff --git a/ts/node/migration/sessionMigrations.ts b/ts/node/migration/sessionMigrations.ts index ae6eb73ca..0dbda50ec 100644 --- a/ts/node/migration/sessionMigrations.ts +++ b/ts/node/migration/sessionMigrations.ts @@ -96,7 +96,6 @@ const LOKI_SCHEMA_VERSIONS = [ updateToSessionSchemaVersion28, updateToSessionSchemaVersion29, updateToSessionSchemaVersion30, - updateToSessionSchemaVersion31, ]; function updateToSessionSchemaVersion1(currentVersion: number, db: BetterSqlite3.Database) { @@ -604,23 +603,26 @@ function updateToSessionSchemaVersion20(currentVersion: number, db: BetterSqlite console.info('found column friendRequestStatus. Dropping it'); db.exec(`ALTER TABLE ${CONVERSATIONS_TABLE} DROP COLUMN friendRequestStatus;`); } + // disable those updates as sqlNode.saveConversation will break if called without the right type of arguments. + // and when called during a migration we won't have the expected arguments. Plus, this migration is almost a year old already + // looking for all private conversations, with a nickname set - const rowsToUpdate = db - .prepare( - `SELECT * FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND (name IS NULL or name = '') AND json_extract(json, '$.nickname') <> '';` - ) - .all(); + // const rowsToUpdate = db + // .prepare( + // `SELECT * FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND (name IS NULL or name = '') AND json_extract(json, '$.nickname') <> '';` + // ) + // .all(); // tslint:disable-next-line: no-void-expression - (rowsToUpdate || []).forEach(r => { - const obj = jsonToObject(r.json); - - // obj.profile.displayName is the display as this user set it. - if (obj?.nickname?.length && obj?.profile?.displayName?.length) { - // this one has a nickname set, but name is unset, set it to the displayName in the lokiProfile if it's exisitng - obj.name = obj.profile.displayName; - sqlNode.saveConversation(obj as ConversationAttributes, db); - } - }); + // (rowsToUpdate || []).forEach(r => { + // const obj = jsonToObject(r.json); + + // // obj.profile.displayName is the display as this user set it. + // if (obj?.nickname?.length && obj?.profile?.displayName?.length) { + // // this one has a nickname set, but name is unset, set it to the displayName in the lokiProfile if it's exisitng + // obj.name = obj.profile.displayName; + // sqlNode.saveConversation(obj as ConversationAttributes, db); + // } + // }); writeSessionSchemaVersion(targetVersion, db); }); console.log(`updateToSessionSchemaVersion${targetVersion}: success!`); @@ -1203,24 +1205,6 @@ function updateToSessionSchemaVersion29(currentVersion: number, db: BetterSqlite console.log(`updateToSessionSchemaVersion${targetVersion}: success!`); } -function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite3.Database) { - const targetVersion = 30; - if (currentVersion >= targetVersion) { - return; - } - - console.log(`updateToSessionSchemaVersion${targetVersion}: starting...`); - // this column will only be set when this is a closed group v3 and we are the admin/got promoted to admin - db.transaction(() => { - db.exec(`ALTER TABLE ${CONVERSATIONS_TABLE} - ADD COLUMN identityPrivateKey TEXT; - `); - writeSessionSchemaVersion(targetVersion, db); - })(); - - console.log(`updateToSessionSchemaVersion${targetVersion}: success!`); -} - function getIdentityKeysDuringMigration(db: BetterSqlite3.Database) { const row = db.prepare(`SELECT * FROM ${ITEMS_TABLE} WHERE id = $id;`).get({ id: 'identityKey', @@ -1426,8 +1410,8 @@ function getBlockedNumbersDuringMigration(db: BetterSqlite3.Database) { } } -function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite3.Database) { - const targetVersion = 31; +function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite3.Database) { + const targetVersion = 30; if (currentVersion >= targetVersion) { return; } @@ -1442,8 +1426,16 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite db.exec( `ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN hidden INTEGER NOT NULL DEFAULT ${toSqliteBoolean( true - )} ;` + )}; + ` ); + // 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; + `); // mark every "active" private chats as not hidden db.prepare( `UPDATE ${CONVERSATIONS_TABLE} SET diff --git a/ts/node/sql.ts b/ts/node/sql.ts index c10424188..68f122b79 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -428,10 +428,6 @@ function saveConversation(data: ConversationAttributes, instance?: BetterSqlite3 groupAdmins, groupModerators, isKickedFromGroup, - subscriberCount, - readCapability, - writeCapability, - uploadCapability, is_medium_group, avatarPointer, avatarImageId, @@ -444,11 +440,10 @@ function saveConversation(data: ConversationAttributes, instance?: BetterSqlite3 displayNameInProfile, conversationIdOrigin, hidden, - // identityPrivateKey, + markedAsUnread, } = formatted; - console.warn('FIXME omit(formatted, identityPrivateKey);'); - const omited = omit(formatted, 'identityPrivateKey', 'markedAsUnread'); + const omited = omit(formatted); const keys = Object.keys(omited); const columnsCommaSeparated = keys.join(', '); const valuesArgs = keys.map(k => `$${k}`).join(', '); @@ -488,10 +483,6 @@ function saveConversation(data: ConversationAttributes, instance?: BetterSqlite3 groupModerators: groupModerators && groupModerators.length ? arrayStrToJson(groupModerators) : '[]', isKickedFromGroup: toSqliteBoolean(isKickedFromGroup), - subscriberCount, - readCapability: toSqliteBoolean(readCapability), - writeCapability: toSqliteBoolean(writeCapability), - uploadCapability: toSqliteBoolean(uploadCapability), is_medium_group: toSqliteBoolean(is_medium_group), avatarPointer, @@ -504,8 +495,8 @@ function saveConversation(data: ConversationAttributes, instance?: BetterSqlite3 avatarInProfile, displayNameInProfile, conversationIdOrigin, - // identityPrivateKey, hidden, + markedAsUnread: toSqliteBoolean(markedAsUnread), }); } @@ -536,14 +527,14 @@ function getConversationById(id: string, instance?: BetterSqlite3.Database) { id, }); - return formatRowOfConversation(row); + return formatRowOfConversation(row, 'getConversationById'); } function getAllConversations() { const rows = assertGlobalInstance() .prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} ORDER BY id ASC;`) .all(); - return (rows || []).map(formatRowOfConversation); + return (rows || []).map(m => formatRowOfConversation(m, 'getAllConversations')); } function getPubkeysInPublicConversation(conversationId: string) { @@ -590,7 +581,7 @@ function searchConversations(query: string) { limit: 50, }); - return (rows || []).map(formatRowOfConversation); + return (rows || []).map(m => formatRowOfConversation(m, 'searchConversations')); } // order by clause is the same as orderByClause but with a table prefix so we cannot reuse it @@ -1089,9 +1080,7 @@ function getUnreadCountByConversation(conversationId: string) { }); if (!row) { - throw new Error( - `getUnreadCountByConversation: Unable to get unread count of ${conversationId}` - ); + throw new Error(`Unable to get unread count of ${conversationId}`); } return row['count(*)']; @@ -2131,12 +2120,15 @@ function cleanUpOldOpengroupsOnStart() { start}ms. Old message count: ${messagesInConvoBefore}, new message count: ${messagesInConvoAfter}` ); - const unreadCount = getUnreadCountByConversation(convoId); - const convoProps = getConversationById(convoId); - if (convoProps) { - convoProps.unreadCount = unreadCount; - saveConversation(convoProps); - } + // no need to update the `unreadCount` during the migration anymore. + // `saveConversation` is broken when called with a argument without all the required fields. + // and this makes little sense as the unreadCount will be updated on opening + // const unreadCount = getUnreadCountByConversation(convoId); + // const convoProps = getConversationById(convoId); + // if (convoProps) { + // convoProps.unreadCount = unreadCount; + // saveConversation(convoProps); + // } } else { console.info( `Not cleaning messages older than 6 months in public convo: ${convoId}. message count: ${messagesInConvoBefore}` diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 0010e7e1e..48111bd4c 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -9,13 +9,14 @@ import { Data } from '../../ts/data/data'; import { SignalService } from '../protobuf'; import { UserUtils } from '../session/utils'; -import { showMessageRequestBanner } from '../state/ducks/userConfig'; +import { showMessageRequestBannerOutsideRedux } from '../state/ducks/userConfig'; import { MessageDirection } from '../models/messageType'; import { LinkPreviews } from '../util/linkPreviews'; import { GoogleChrome } from '../util'; import { ConversationTypeEnum } from '../models/conversationAttributes'; import { getUsBlindedInThatServer } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys'; import { ProfileManager } from '../session/profile_manager/ProfileManager'; +import { getHideMessageRequestBannerOutsideRedux } from '../state/selectors/userConfig'; function contentTypeSupported(type: string): boolean { const Chrome = GoogleChrome; @@ -258,9 +259,9 @@ async function handleRegularMessage( if ( conversation.isIncomingRequest() && isFirstRequestMessage && - window.inboxStore?.getState().userConfig.hideMessageRequests + getHideMessageRequestBannerOutsideRedux() ) { - window.inboxStore?.dispatch(showMessageRequestBanner()); + showMessageRequestBannerOutsideRedux(); } // For edge case when messaging a client that's unable to explicitly send request approvals diff --git a/ts/session/apis/open_group_api/sogsv3/sogsApiV3.ts b/ts/session/apis/open_group_api/sogsv3/sogsApiV3.ts index e337c8648..5a6e90c7b 100644 --- a/ts/session/apis/open_group_api/sogsv3/sogsApiV3.ts +++ b/ts/session/apis/open_group_api/sogsv3/sogsApiV3.ts @@ -119,7 +119,7 @@ async function handlePollInfoResponse( read, write, upload, - subscriberCount: active_users, + active_users: active_users, details: pick( details, 'admins', diff --git a/ts/session/conversations/createClosedGroup.ts b/ts/session/conversations/createClosedGroup.ts index e99cd7c1a..f95184578 100644 --- a/ts/session/conversations/createClosedGroup.ts +++ b/ts/session/conversations/createClosedGroup.ts @@ -25,6 +25,10 @@ import { getConversationController } from './ConversationController'; export async function createClosedGroup(groupName: string, members: Array, isV3: boolean) { const setOfMembers = new Set(members); + if (isV3) { + throw new Error('groupv3 is not supported yet'); + } + const us = UserUtils.getOurPubKeyStrFromCache(); const identityKeyPair = await generateGroupV3Keypair(); @@ -63,17 +67,6 @@ export async function createClosedGroup(groupName: string, members: Array; - activeAt?: number; - expireTimer?: number | null; - blocked?: boolean; - admins?: Array; - weWereJustAdded?: boolean; -}; - export interface GroupDiff extends MemberChanges { newName?: string; } @@ -230,11 +217,7 @@ function buildGroupDiff(convo: ConversationModel, update: GroupInfo): GroupDiff return groupDiff; } -function isV3(details: GroupInfo | GroupInfoV3): details is GroupInfoV3 { - return (details as GroupInfoV3).isV3 === true; -} - -export async function updateOrCreateClosedGroup(details: GroupInfo | GroupInfoV3) { +export async function updateOrCreateClosedGroup(details: GroupInfo) { const { id, weWereJustAdded, expireTimer } = details; const conversation = await getConversationController().getOrCreateAndWait( @@ -245,7 +228,6 @@ export async function updateOrCreateClosedGroup(details: GroupInfo | GroupInfoV3 const updates: Pick< ConversationAttributes, | 'type' - // | 'identityPrivateKey' | 'members' | 'displayNameInProfile' | 'is_medium_group' @@ -256,15 +238,13 @@ export async function updateOrCreateClosedGroup(details: GroupInfo | GroupInfoV3 > = { displayNameInProfile: details.name, members: details.members, - type: isV3(details) ? ConversationTypeEnum.GROUPV3 : ConversationTypeEnum.GROUP, + type: ConversationTypeEnum.GROUP, is_medium_group: true, active_at: details.activeAt ? details.activeAt : 0, left: details.activeAt ? false : true, lastJoinedTimestamp: details.activeAt && weWereJustAdded ? Date.now() : details.activeAt || 0, hidden: false, - // identityPrivateKey: isV3(details) ? details.identityPrivateKey : undefined, }; - console.warn('updates', updates); conversation.set(updates); diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index faea4d895..e38831aac 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -251,7 +251,6 @@ export interface ReduxConversationType { isBlocked?: boolean; isHidden: boolean; isKickedFromGroup?: boolean; - subscriberCount?: number; left?: boolean; avatarPath?: string | null; // absolute filepath to the avatar groupAdmins?: Array; // admins for closed groups and admins for open groups @@ -271,9 +270,6 @@ export interface ReduxConversationType { // Should only be present on opengroups - the capabilities we have on this room. capabilities?: Array; - readCapability?: boolean; - writeCapability?: boolean; - uploadCapability?: boolean; } export interface NotificationForConvoOption { diff --git a/ts/state/ducks/index.ts b/ts/state/ducks/index.ts deleted file mode 100644 index 443edb61e..000000000 --- a/ts/state/ducks/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as CallDucks from './call'; -import * as conversationDucks from './conversations'; -import * as defaultRoomDucks from './defaultRooms'; -import * as DialogsDucks from './modalDialog'; -import * as OnionDucks from './onion'; -import * as PrimaryColorDucks from './primaryColor'; -import * as SearchDucks from './search'; -import * as SectionDucks from './section'; -import * as StagedAttachmentDucks from './stagedAttachments'; -import * as ThemeDucks from './theme'; -import * as TimerOptionsDucks from './timerOptions'; -import * as UserDucks from './user'; -import * as UserConfigDucks from './userConfig'; - -export { - CallDucks, - DialogsDucks, - OnionDucks, - PrimaryColorDucks, - SearchDucks, - SectionDucks, - StagedAttachmentDucks, - ThemeDucks, - TimerOptionsDucks, - UserConfigDucks, - UserDucks, - conversationDucks, - defaultRoomDucks, -}; diff --git a/ts/state/ducks/sogsRoomInfo.tsx b/ts/state/ducks/sogsRoomInfo.tsx new file mode 100644 index 000000000..8be4d6ec6 --- /dev/null +++ b/ts/state/ducks/sogsRoomInfo.tsx @@ -0,0 +1,62 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +type RoomInfo = { + canWrite: boolean; + subscriberCount: number; +}; + +export type SogsRoomInfoState = { + rooms: Record; +}; + +export const initialSogsRoomInfoState: SogsRoomInfoState = { + rooms: {}, +}; + +function addEmptyEntryIfNeeded(state: any, convoId: string) { + if (!state.rooms[convoId]) { + state.rooms[convoId] = { canWrite: true, subscriberCount: 0 }; + } +} + +/** + * This slice is the one holding the memory-only infos of sogs room. This includes + * - writeCapability + * - subscriberCount + */ +const sogsRoomInfosSlice = createSlice({ + name: 'sogsRoomInfos', + initialState: initialSogsRoomInfoState, + reducers: { + setSubscriberCount(state, action: PayloadAction<{ convoId: string; subscriberCount: number }>) { + addEmptyEntryIfNeeded(state, action.payload.convoId); + if (isFinite(action.payload.subscriberCount)) { + state.rooms[action.payload.convoId].subscriberCount = action.payload.subscriberCount; + } + return state; + }, + setCanWrite(state, action: PayloadAction<{ convoId: string; canWrite: boolean }>) { + addEmptyEntryIfNeeded(state, action.payload.convoId); + state.rooms[action.payload.convoId].canWrite = !!action.payload.canWrite; + + return state; + }, + }, +}); + +const { actions, reducer } = sogsRoomInfosSlice; +const { setSubscriberCount, setCanWrite } = actions; + +export const ReduxSogsRoomInfos = { + setSubscriberCountOutsideRedux, + setCanWriteOutsideRedux, + sogsRoomInfoReducer: reducer, +}; + +function setSubscriberCountOutsideRedux(convoId: string, subscriberCount: number) { + window.inboxStore?.dispatch(setSubscriberCount({ convoId, subscriberCount })); +} + +function setCanWriteOutsideRedux(convoId: string, canWrite: boolean) { + window.inboxStore?.dispatch(setCanWrite({ convoId, canWrite })); +} diff --git a/ts/state/ducks/userConfig.tsx b/ts/state/ducks/userConfig.tsx index 7881ffa62..502367b87 100644 --- a/ts/state/ducks/userConfig.tsx +++ b/ts/state/ducks/userConfig.tsx @@ -26,9 +26,6 @@ const userConfigSlice = createSlice({ disableRecoveryPhrasePrompt: state => { state.showRecoveryPhrasePrompt = false; }, - toggleMessageRequests: state => { - state.hideMessageRequests = !state.hideMessageRequests; - }, showMessageRequestBanner: state => { state.hideMessageRequests = false; }, @@ -42,8 +39,10 @@ const { actions, reducer } = userConfigSlice; export const { toggleAudioAutoplay, disableRecoveryPhrasePrompt, - toggleMessageRequests, - showMessageRequestBanner, hideMessageRequestBanner, } = actions; export const userConfigReducer = reducer; + +export function showMessageRequestBannerOutsideRedux() { + window.inboxStore?.dispatch(actions.showMessageRequestBanner()); +} diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index 0029e2725..da394d25f 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -7,6 +7,7 @@ import { reducer as theme } from './ducks/theme'; import { reducer as primaryColor } from './ducks/primaryColor'; import { reducer as section, SectionStateType } from './ducks/section'; import { defaultRoomReducer as defaultRooms, DefaultRoomsState } from './ducks/defaultRooms'; +import { ReduxSogsRoomInfos, SogsRoomInfoState } from './ducks/sogsRoomInfo'; import { callReducer as call, CallStateType } from './ducks/call'; import { defaultOnionReducer as onionPaths, OnionState } from './ducks/onion'; @@ -33,6 +34,7 @@ export type StateType = { timerOptions: TimerOptionsState; stagedAttachments: StagedAttachmentsStateType; call: CallStateType; + sogsRoomInfo: SogsRoomInfoState; }; export const reducers = { @@ -49,6 +51,7 @@ export const reducers = { timerOptions, stagedAttachments, call, + sogsRoomInfo: ReduxSogsRoomInfos.sogsRoomInfoReducer, }; // Making this work would require that our reducer signature supported AnyAction, not diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 3a5175369..a67d3a74c 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -37,6 +37,7 @@ import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversa import { MessageReactsSelectorProps } from '../../components/conversation/message/message-content/MessageReactions'; import { filter, isEmpty, pick, sortBy } from 'lodash'; +import { getCanWrite, getSubscriberCount } from './sogsRoomInfo'; export const getConversations = (state: StateType): ConversationsStateType => state.conversations; @@ -74,22 +75,21 @@ export const getSelectedConversationIsPublic = createSelector( } ); -export const getIsTypingEnabled = createSelector( - getConversations, - getSelectedConversationKey, - (state: ConversationsStateType, selectedConvoPubkey?: string): boolean => { - if (!selectedConvoPubkey) { - return false; - } - const selectedConvo = state.conversationLookup[selectedConvoPubkey]; - if (!selectedConvo) { - return false; - } - const { isBlocked, isKickedFromGroup, left, isPublic, writeCapability } = selectedConvo; - - return !(isBlocked || isKickedFromGroup || left || (isPublic && !writeCapability)); +export function getIsTypingEnabled(state: StateType) { + const selectedConvoPubkey = getSelectedConversationKey(state); + if (!selectedConvoPubkey) { + return false; } -); + const selectedConvo = state.conversations.conversationLookup[selectedConvoPubkey]; + if (!selectedConvo) { + return false; + } + const canWrite = getCanWrite(state, selectedConvoPubkey); + const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo; + + return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWrite)); +} + /** * Returns true if the current conversation selected is a group conversation. * Returns false if the current conversation selected is not a group conversation, or none are selected @@ -551,23 +551,32 @@ export const getUnreadMessageCount = createSelector(getLeftPaneLists, (state): n return state.unreadCount; }); -export const getConversationHeaderTitleProps = createSelector(getSelectedConversation, (state): - | ConversationHeaderTitleProps - | undefined => { - if (!state) { +export const getConversationHeaderTitleProps = ( + state: StateType +): ConversationHeaderTitleProps | undefined => { + const convo = getSelectedConversation(state); + if (!convo) { return undefined; } + return { - isKickedFromGroup: !!state.isKickedFromGroup, - conversationKey: state.id, - isMe: !!state.isMe, - members: state.members || [], - isPublic: !!state.isPublic, - subscriberCount: state.subscriberCount, - isGroup: isOpenOrClosedGroup(state.type), - currentNotificationSetting: state.currentNotificationSetting, + isKickedFromGroup: !!convo.isKickedFromGroup, + conversationKey: convo.id, + isMe: !!convo.isMe, + members: convo.members || [], + isPublic: !!convo.isPublic, + isGroup: isOpenOrClosedGroup(convo.type), + currentNotificationSetting: convo.currentNotificationSetting, }; -}); +}; + +export const getCurrentSubscriberCount = (state: StateType): number | undefined => { + const convo = getSelectedConversation(state); + if (!convo) { + return undefined; + } + return getSubscriberCount(state, convo.id); +}; /** * Returns the formatted text for notification setting. diff --git a/ts/state/selectors/sogsRoomInfo.ts b/ts/state/selectors/sogsRoomInfo.ts new file mode 100644 index 000000000..34b61be69 --- /dev/null +++ b/ts/state/selectors/sogsRoomInfo.ts @@ -0,0 +1,36 @@ +import { isNil } from 'lodash'; +import { SogsRoomInfoState } from '../ducks/sogsRoomInfo'; +import { StateType } from '../reducer'; + +const getSogsRoomInfoState = (state: StateType): SogsRoomInfoState => state.sogsRoomInfo; + +export function getCanWrite(state: StateType, selectedConvo?: string): boolean { + if (!selectedConvo) { + return false; + } + + const canWrite = getSogsRoomInfoState(state).rooms[selectedConvo]?.canWrite; + // if there is no entry in the redux slice, consider it true (as this selector will be hit for non sogs convo too) + return isNil(canWrite) ? true : canWrite; +} + +export function getSubscriberCount(state: StateType, selectedConvo?: string): number { + if (!selectedConvo) { + return 0; + } + + const subscriberCount = getSogsRoomInfoState(state).rooms[selectedConvo]?.subscriberCount; + // if there is no entry in the redux slice, consider it 0 (as this selector will be hit for non sogs convo too) + return isNil(subscriberCount) ? 0 : subscriberCount; +} + +export function getSubscriberCountOutsideRedux(convoId: string) { + const state = window.inboxStore?.getState(); + + return state ? getSubscriberCount(state, convoId) : 0; +} + +export function getCanWriteOutsideRedux(convoId: string) { + const state = window.inboxStore?.getState(); + return state ? getCanWrite(state, convoId) : false; +} diff --git a/ts/state/selectors/userConfig.ts b/ts/state/selectors/userConfig.ts index 2c1960b96..377b7fa9b 100644 --- a/ts/state/selectors/userConfig.ts +++ b/ts/state/selectors/userConfig.ts @@ -1,20 +1,17 @@ import { StateType } from '../reducer'; -import { UserConfigState } from '../ducks/userConfig'; -import { createSelector } from '@reduxjs/toolkit'; -export const getUserConfig = (state: StateType): UserConfigState => state.userConfig; +export const getAudioAutoplay = (state: StateType): boolean => state.userConfig.audioAutoplay; -export const getAudioAutoplay = createSelector( - getUserConfig, - (state: UserConfigState): boolean => state.audioAutoplay -); +export const getShowRecoveryPhrasePrompt = (state: StateType): boolean => + state.userConfig?.showRecoveryPhrasePrompt || false; -export const getShowRecoveryPhrasePrompt = createSelector( - getUserConfig, - (state: UserConfigState): boolean => state.showRecoveryPhrasePrompt -); +export const getHideMessageRequestBanner = (state: StateType): boolean => { + // I am not too sure why, but it seems that state.userConfig is not set early enough and we try to somehow fetch this too early? + return state.userConfig?.hideMessageRequests || false; +}; -export const getHideMessageRequestBanner = createSelector( - getUserConfig, - (state: UserConfigState): boolean => state.hideMessageRequests -); +export const getHideMessageRequestBannerOutsideRedux = (): boolean => { + const state = window.inboxStore?.getState(); + + return state ? getHideMessageRequestBanner(state) : true; +}; diff --git a/ts/test/session/unit/models/ConversationModels_test.ts b/ts/test/session/unit/models/ConversationModels_test.ts index 048c8ba07..a1474bc35 100644 --- a/ts/test/session/unit/models/ConversationModels_test.ts +++ b/ts/test/session/unit/models/ConversationModels_test.ts @@ -84,21 +84,6 @@ describe('fillConvoAttributesWithDefaults', () => { }); }); - describe('subscriberCount', () => { - it('initialize subscriberCount if not given', () => { - expect(fillConvoAttributesWithDefaults({} as ConversationAttributes)).to.have.deep.property( - 'subscriberCount', - 0 - ); - }); - - it('do not override subscriberCount if given', () => { - expect( - fillConvoAttributesWithDefaults({ subscriberCount: 123 } as ConversationAttributes) - ).to.have.deep.property('subscriberCount', 123); - }); - }); - describe('expireTimer', () => { it('initialize expireTimer if not given', () => { expect(fillConvoAttributesWithDefaults({} as ConversationAttributes)).to.have.deep.property( diff --git a/ts/test/session/unit/models/formatRowOfConversation_test.ts b/ts/test/session/unit/models/formatRowOfConversation_test.ts index 5468b1a81..3265e034e 100644 --- a/ts/test/session/unit/models/formatRowOfConversation_test.ts +++ b/ts/test/session/unit/models/formatRowOfConversation_test.ts @@ -13,69 +13,79 @@ import { formatRowOfConversation } from '../../../../node/database_utility'; describe('formatRowOfConversation', () => { describe('isTrustedForAttachmentDownload', () => { it('initialize isTrustedForAttachmentDownload if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property( + expect(formatRowOfConversation({}, 'test')).to.have.deep.property( 'isTrustedForAttachmentDownload', false ); }); it('do not override isTrustedForAttachmentDownload if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ isTrustedForAttachmentDownload: 1 })).to.have.deep.property( - 'isTrustedForAttachmentDownload', - true - ); + expect( + formatRowOfConversation({ isTrustedForAttachmentDownload: 1 }, 'test') + ).to.have.deep.property('isTrustedForAttachmentDownload', true); }); it('do not override isTrustedForAttachmentDownload if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ isTrustedForAttachmentDownload: 0 })).to.have.deep.property( - 'isTrustedForAttachmentDownload', - false - ); + expect( + formatRowOfConversation({ isTrustedForAttachmentDownload: 0 }, 'test') + ).to.have.deep.property('isTrustedForAttachmentDownload', false); }); }); describe('isPinned', () => { it('initialize isPinned if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('isPinned', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('isPinned', false); }); it('do not override isPinned if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ isPinned: 1 })).to.have.deep.property('isPinned', true); + expect(formatRowOfConversation({ isPinned: 1 }, 'test')).to.have.deep.property( + 'isPinned', + true + ); }); it('do not override isPinned if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ isPinned: 0 })).to.have.deep.property('isPinned', false); + expect(formatRowOfConversation({ isPinned: 0 }, 'test')).to.have.deep.property( + 'isPinned', + false + ); }); }); describe('isApproved', () => { it('initialize isApproved if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('isApproved', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('isApproved', false); }); it('do not override isApproved if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ isApproved: 1 })).to.have.deep.property('isApproved', true); + expect(formatRowOfConversation({ isApproved: 1 }, 'test')).to.have.deep.property( + 'isApproved', + true + ); }); it('do not override isApproved if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ isApproved: 0 })).to.have.deep.property('isApproved', false); + expect(formatRowOfConversation({ isApproved: 0 }, 'test')).to.have.deep.property( + 'isApproved', + false + ); }); }); describe('didApproveMe', () => { it('initialize didApproveMe if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('didApproveMe', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('didApproveMe', false); }); it('do not override didApproveMe if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ didApproveMe: 1 })).to.have.deep.property( + expect(formatRowOfConversation({ didApproveMe: 1 }, 'test')).to.have.deep.property( 'didApproveMe', true ); }); it('do not override didApproveMe if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ didApproveMe: 0 })).to.have.deep.property( + expect(formatRowOfConversation({ didApproveMe: 0 }, 'test')).to.have.deep.property( 'didApproveMe', false ); @@ -84,18 +94,18 @@ describe('formatRowOfConversation', () => { describe('is_medium_group', () => { it('initialize is_medium_group if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('is_medium_group', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('is_medium_group', false); }); it('do not override is_medium_group if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ is_medium_group: 1 })).to.have.deep.property( + expect(formatRowOfConversation({ is_medium_group: 1 }, 'test')).to.have.deep.property( 'is_medium_group', true ); }); it('do not override is_medium_group if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ is_medium_group: 0 })).to.have.deep.property( + expect(formatRowOfConversation({ is_medium_group: 0 }, 'test')).to.have.deep.property( 'is_medium_group', false ); @@ -104,18 +114,18 @@ describe('formatRowOfConversation', () => { describe('mentionedUs', () => { it('initialize mentionedUs if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('mentionedUs', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('mentionedUs', false); }); it('do not override mentionedUs if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ mentionedUs: 1 })).to.have.deep.property( + expect(formatRowOfConversation({ mentionedUs: 1 }, 'test')).to.have.deep.property( 'mentionedUs', true ); }); it('do not override mentionedUs if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ mentionedUs: 0 })).to.have.deep.property( + expect(formatRowOfConversation({ mentionedUs: 0 }, 'test')).to.have.deep.property( 'mentionedUs', false ); @@ -124,18 +134,18 @@ describe('formatRowOfConversation', () => { describe('isKickedFromGroup', () => { it('initialize isKickedFromGroup if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('isKickedFromGroup', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('isKickedFromGroup', false); }); it('do not override isKickedFromGroup if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ isKickedFromGroup: 1 })).to.have.deep.property( + expect(formatRowOfConversation({ isKickedFromGroup: 1 }, 'test')).to.have.deep.property( 'isKickedFromGroup', true ); }); it('do not override isKickedFromGroup if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ isKickedFromGroup: 0 })).to.have.deep.property( + expect(formatRowOfConversation({ isKickedFromGroup: 0 }, 'test')).to.have.deep.property( 'isKickedFromGroup', false ); @@ -144,28 +154,28 @@ describe('formatRowOfConversation', () => { describe('left', () => { it('initialize left if they are not given', () => { - expect(formatRowOfConversation({})).to.have.deep.property('left', false); + expect(formatRowOfConversation({}, 'test')).to.have.deep.property('left', false); }); it('do not override left if they are set in the row as integer: true', () => { - expect(formatRowOfConversation({ left: 1 })).to.have.deep.property('left', true); + expect(formatRowOfConversation({ left: 1 }, 'test')).to.have.deep.property('left', true); }); it('do not override left if they are set in the row as integer: false', () => { - expect(formatRowOfConversation({ left: 0 })).to.have.deep.property('left', false); + expect(formatRowOfConversation({ left: 0 }, 'test')).to.have.deep.property('left', false); }); }); describe('row', () => { it('row null returns null', () => { - expect(formatRowOfConversation(null as any)).to.be.equal( + expect(formatRowOfConversation(null as any, 'test')).to.be.equal( null, 'formatRowOfConversation with null should return null' ); }); it('row undefined returns null', () => { - expect(formatRowOfConversation(undefined as any)).to.be.equal( + expect(formatRowOfConversation(undefined as any, 'test')).to.be.equal( null, 'formatRowOfConversation with undefined should return null' ); @@ -174,21 +184,21 @@ describe('formatRowOfConversation', () => { describe('groupAdmins', () => { it('groupAdmins undefined fills it with []', () => { - expect(formatRowOfConversation({ groupAdmins: undefined })).to.be.have.deep.property( + expect(formatRowOfConversation({ groupAdmins: undefined }, 'test')).to.be.have.deep.property( 'groupAdmins', [] ); }); it('groupAdmins null fills it with []', () => { - expect(formatRowOfConversation({ groupAdmins: null })).to.be.have.deep.property( + expect(formatRowOfConversation({ groupAdmins: null }, 'test')).to.be.have.deep.property( 'groupAdmins', [] ); }); it('groupAdmins [] fills it with []', () => { - expect(formatRowOfConversation({ groupAdmins: '[]' })).to.be.have.deep.property( + expect(formatRowOfConversation({ groupAdmins: '[]' }, 'test')).to.be.have.deep.property( 'groupAdmins', [] ); @@ -196,86 +206,96 @@ describe('formatRowOfConversation', () => { it('groupAdmins ["12345"] from db as string', () => { expect( - formatRowOfConversation({ groupAdmins: '["12345"]' }) + formatRowOfConversation({ groupAdmins: '["12345"]' }, 'test') ).to.be.have.deep.property('groupAdmins', ['12345']); }); it('groupAdmins ["12345", "52345"] fills it with []', () => { expect( - formatRowOfConversation({ groupAdmins: '["12345", "52345"]' }) + formatRowOfConversation({ groupAdmins: '["12345", "52345"]' }, 'test') ).to.be.have.deep.property('groupAdmins', ['12345', '52345']); }); }); describe('members', () => { it('members undefined fills it with []', () => { - expect(formatRowOfConversation({ members: undefined })).to.be.have.deep.property( + expect(formatRowOfConversation({ members: undefined }, 'test')).to.be.have.deep.property( 'members', [] ); }); it('members null fills it with []', () => { - expect(formatRowOfConversation({ members: null })).to.be.have.deep.property('members', []); + expect(formatRowOfConversation({ members: null }, 'test')).to.be.have.deep.property( + 'members', + [] + ); }); it('members [] fills it with []', () => { - expect(formatRowOfConversation({ members: '[]' })).to.be.have.deep.property('members', []); + expect(formatRowOfConversation({ members: '[]' }, 'test')).to.be.have.deep.property( + 'members', + [] + ); }); it('members ["12345"] from db as string', () => { - expect(formatRowOfConversation({ members: '["12345"]' })).to.be.have.deep.property( - 'members', - ['12345'] - ); + expect( + formatRowOfConversation({ members: '["12345"]' }, 'test') + ).to.be.have.deep.property('members', ['12345']); }); it('members ["12345", "52345"] fills it with []', () => { expect( - formatRowOfConversation({ members: '["12345", "52345"]' }) + formatRowOfConversation({ members: '["12345", "52345"]' }, 'test') ).to.be.have.deep.property('members', ['12345', '52345']); }); }); describe('zombies', () => { it('zombies undefined fills it with []', () => { - expect(formatRowOfConversation({ zombies: undefined })).to.be.have.deep.property( + expect(formatRowOfConversation({ zombies: undefined }, 'test')).to.be.have.deep.property( 'zombies', [] ); }); it('zombies null fills it with []', () => { - expect(formatRowOfConversation({ zombies: null })).to.be.have.deep.property('zombies', []); + expect(formatRowOfConversation({ zombies: null }, 'test')).to.be.have.deep.property( + 'zombies', + [] + ); }); it('zombies [] fills it with []', () => { - expect(formatRowOfConversation({ zombies: '[]' })).to.be.have.deep.property('zombies', []); + expect(formatRowOfConversation({ zombies: '[]' }, 'test')).to.be.have.deep.property( + 'zombies', + [] + ); }); it('zombies ["12345"] from db as string', () => { - expect(formatRowOfConversation({ zombies: '["12345"]' })).to.be.have.deep.property( - 'zombies', - ['12345'] - ); + expect( + formatRowOfConversation({ zombies: '["12345"]' }, 'test') + ).to.be.have.deep.property('zombies', ['12345']); }); it('zombies ["12345", "52345"] fills it with ["12345", "52345"]', () => { expect( - formatRowOfConversation({ zombies: '["12345", "52345"]' }) + formatRowOfConversation({ zombies: '["12345", "52345"]' }, 'test') ).to.be.have.deep.property('zombies', ['12345', '52345']); }); }); it('throws an error if a key is not expected', () => { - expect(() => formatRowOfConversation({ not_valid: undefined })).throws( + expect(() => formatRowOfConversation({ not_valid: undefined }, 'test')).throws( 'formatRowOfConversation: an invalid key was given in the record: not_valid' ); }); it('throws an error if a key is not expected but has other valid keys', () => { expect(() => - formatRowOfConversation({ triggerNotificationsFor: 'all', not_valid: undefined }) + formatRowOfConversation({ triggerNotificationsFor: 'all', not_valid: undefined }, 'test') ).throws('formatRowOfConversation: an invalid key was given in the record: not_valid'); }); @@ -291,7 +311,8 @@ describe('formatRowOfConversation', () => { avatarPointer: 'avatarPointer', avatarInProfile: 'avatarInProfile', avatarImageId: 1234, - } as ConversationAttributes) + } as ConversationAttributes), + 'test' ) ).have.deep.property('displayNameInProfile', 'displayNameInProfile'); @@ -306,7 +327,8 @@ describe('formatRowOfConversation', () => { avatarPointer: 'avatarPointer', avatarInProfile: 'avatarInProfile', avatarImageId: 1234, - } as ConversationAttributes) + } as ConversationAttributes), + 'test' ) ).have.deep.property('displayNameInProfile', 'displayNameInProfile'); }); diff --git a/ts/test/session/unit/selectors/conversations_test.ts b/ts/test/session/unit/selectors/conversations_test.ts index 34a4c9549..ef230f79e 100644 --- a/ts/test/session/unit/selectors/conversations_test.ts +++ b/ts/test/session/unit/selectors/conversations_test.ts @@ -28,7 +28,6 @@ describe('state/selectors/conversations', () => { left: false, hasNickname: false, isPublic: false, - subscriberCount: 0, currentNotificationSetting: 'all', weAreAdmin: false, isGroup: false, @@ -58,7 +57,6 @@ describe('state/selectors/conversations', () => { left: false, hasNickname: false, isPublic: false, - subscriberCount: 0, currentNotificationSetting: 'all', weAreAdmin: false, isGroup: false, @@ -88,7 +86,6 @@ describe('state/selectors/conversations', () => { left: false, hasNickname: false, isPublic: false, - subscriberCount: 0, currentNotificationSetting: 'all', weAreAdmin: false, isGroup: false, @@ -118,7 +115,6 @@ describe('state/selectors/conversations', () => { left: false, hasNickname: false, isPublic: false, - subscriberCount: 0, currentNotificationSetting: 'all', weAreAdmin: false, isGroup: false, @@ -148,7 +144,6 @@ describe('state/selectors/conversations', () => { left: false, hasNickname: false, isPublic: false, - subscriberCount: 0, expireTimer: 0, currentNotificationSetting: 'all', weAreAdmin: false, @@ -193,7 +188,6 @@ describe('state/selectors/conversations', () => { isBlocked: false, isKickedFromGroup: false, left: false, - subscriberCount: 0, expireTimer: 0, currentNotificationSetting: 'all', weAreAdmin: false, @@ -224,7 +218,6 @@ describe('state/selectors/conversations', () => { isBlocked: false, isKickedFromGroup: false, left: false, - subscriberCount: 0, expireTimer: 0, currentNotificationSetting: 'all', weAreAdmin: false, @@ -254,7 +247,6 @@ describe('state/selectors/conversations', () => { isBlocked: false, isKickedFromGroup: false, left: false, - subscriberCount: 0, expireTimer: 0, currentNotificationSetting: 'all', weAreAdmin: false, @@ -285,7 +277,6 @@ describe('state/selectors/conversations', () => { isBlocked: false, isKickedFromGroup: false, left: false, - subscriberCount: 0, expireTimer: 0, currentNotificationSetting: 'all', weAreAdmin: false, @@ -316,7 +307,6 @@ describe('state/selectors/conversations', () => { isKickedFromGroup: false, left: false, - subscriberCount: 0, expireTimer: 0, currentNotificationSetting: 'all', weAreAdmin: false, diff --git a/ts/util/accountManager.ts b/ts/util/accountManager.ts index e1d35b959..63f4b3c4f 100644 --- a/ts/util/accountManager.ts +++ b/ts/util/accountManager.ts @@ -179,8 +179,13 @@ async function createAccount(identityKeyPair: SessionKeyPair) { async function registrationDone(ourPubkey: string, displayName: string) { window?.log?.info('registration done'); + // initializeLibSessionUtilWrappers needs our publicKey to be set await Storage.put('primaryDevicePubKey', ourPubkey); - + try { + await LibSessionUtil.initializeLibSessionUtilWrappers(); + } catch (e) { + window.log.warn('LibSessionUtil.initializeLibSessionUtilWrappers failed with', e.message); + } // Ensure that we always have a conversation for ourself const conversation = await getConversationController().getOrCreateAndWait( ourPubkey, @@ -199,11 +204,7 @@ async function registrationDone(ourPubkey: string, displayName: string) { window.inboxStore?.dispatch(userActions.userChanged(user)); await Registration.markDone(); - try { - await LibSessionUtil.initializeLibSessionUtilWrappers(); - } catch (e) { - window.log.warn('LibSessionUtil.initializeLibSessionUtilWrappers failed with', e.message); - } + window?.log?.info('dispatching registration event'); trigger('registration_done'); }