From dc3e8450e9a45e4f1b80ff326fa03f978366f0fa Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 27 Jun 2023 11:08:00 +0200 Subject: [PATCH] fix: memoize selected conversation props to avoid unneeded rerenders --- package.json | 1 + password_preload.js | 8 --- .../SessionMessagesListContainer.tsx | 6 +- .../composition/CompositionBox.tsx | 7 +- ts/data/data.ts | 64 ++++++++++++------- ts/node/sql.ts | 9 +-- ts/state/selectors/conversations.ts | 13 +++- ts/state/selectors/selectedConversation.ts | 7 +- ts/state/smart/SessionConversation.ts | 6 +- 9 files changed, 66 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index 8f496c186..9dcd755e4 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "scripts": { "start-prod": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod$MULTI electron .", + "start-dev": "cross-env NODE_ENV=development NODE_APP_INSTANCE=devprod$MULTI electron .", "build-everything": "yarn clean && yarn protobuf && yarn update-git-info && yarn sass && tsc && yarn build:workers", "build-everything:watch": "yarn clean && yarn protobuf && yarn update-git-info && yarn sass && yarn build:workers && tsc -w", "build:workers": "yarn worker:utils && yarn worker:libsession", diff --git a/password_preload.js b/password_preload.js index 18e3aad60..7463ad9b0 100644 --- a/password_preload.js +++ b/password_preload.js @@ -20,14 +20,6 @@ window.getEnvironment = () => config.environment; window.getVersion = () => config.version; window.getAppInstance = () => config.appInstance; -const { SessionPasswordPrompt } = require('./ts/components/SessionPasswordPrompt'); - -window.Signal = { - Components: { - SessionPasswordPrompt, - }, -}; - window.clearLocalData = async () => { window.log.info('reset database'); ipcRenderer.send('resetDatabase'); diff --git a/ts/components/conversation/SessionMessagesListContainer.tsx b/ts/components/conversation/SessionMessagesListContainer.tsx index fccacbddf..efadb4958 100644 --- a/ts/components/conversation/SessionMessagesListContainer.tsx +++ b/ts/components/conversation/SessionMessagesListContainer.tsx @@ -17,12 +17,10 @@ import { import { StateType } from '../../state/reducer'; import { getQuotedMessageToAnimate, + getSelectedConversation, getSortedMessagesOfSelectedConversation, } from '../../state/selectors/conversations'; -import { - getSelectedConversation, - getSelectedConversationKey, -} from '../../state/selectors/selectedConversation'; +import { getSelectedConversationKey } from '../../state/selectors/selectedConversation'; import { SessionMessagesList } from './SessionMessagesList'; import { TypingBubble } from './TypingBubble'; import { ConversationMessageRequestButtons } from './MessageRequestButtons'; diff --git a/ts/components/conversation/composition/CompositionBox.tsx b/ts/components/conversation/composition/CompositionBox.tsx index 7a0547e5e..a01548740 100644 --- a/ts/components/conversation/composition/CompositionBox.tsx +++ b/ts/components/conversation/composition/CompositionBox.tsx @@ -31,7 +31,11 @@ import { ToastUtils } from '../../../session/utils'; import { ReduxConversationType } from '../../../state/ducks/conversations'; import { removeAllStagedAttachmentsInConversation } from '../../../state/ducks/stagedAttachments'; import { StateType } from '../../../state/reducer'; -import { getMentionsInput, getQuotedMessage } from '../../../state/selectors/conversations'; +import { + getMentionsInput, + getQuotedMessage, + getSelectedConversation, +} from '../../../state/selectors/conversations'; import { AttachmentUtil } from '../../../util'; import { Flex } from '../../basic/Flex'; import { CaptionEditor } from '../../CaptionEditor'; @@ -53,7 +57,6 @@ import styled from 'styled-components'; import { FixedBaseEmoji } from '../../../types/Reaction'; import { getSelectedCanWrite, - getSelectedConversation, getSelectedConversationKey, } from '../../../state/selectors/selectedConversation'; import { SettingsKey } from '../../../data/settings-key'; diff --git a/ts/data/data.ts b/ts/data/data.ts index 07acd7a16..6ecbfe3bd 100644 --- a/ts/data/data.ts +++ b/ts/data/data.ts @@ -497,35 +497,51 @@ async function getSeenMessagesByHashList(hashes: Array): Promise { } async function removeAllMessagesInConversation(conversationId: string): Promise { + const startFunction = Date.now(); let start = Date.now(); - const messages = await getLastMessagesByConversation(conversationId, 50, false); - window.log.info( - `removeAllMessagesInConversation ${conversationId} ${messages.length} took ${Date.now() - - start}` - ); - if (!messages.length) { - return; - } - // Note: It's very important that these models are fully hydrated because - // we need to delete all associated on-disk files along with the database delete. - // eslint-disable-next-line no-await-in-loop - - start = Date.now(); - for (let index = 0; index < messages.length; index++) { - const message = messages.at(index); - await message.cleanup(); - } - window.log.info( - `removeAllMessagesInConversation messages.cleanup() ${conversationId} took ${Date.now() - - start}ms` - ); - start = Date.now(); + let messages; + do { + // Yes, we really want the await in the loop. We're deleting 500 at a + // time so we don't use too much memory. + // eslint-disable-next-line no-await-in-loop + messages = await getLastMessagesByConversation(conversationId, 1000, false); + if (!messages.length) { + return; + } + window.log.info( + `removeAllMessagesInConversation getLastMessagesByConversation ${conversationId} ${ + messages.length + } took ${Date.now() - start}ms` + ); + + // Note: It's very important that these models are fully hydrated because + // we need to delete all associated on-disk files along with the database delete. + const ids = messages.map(message => message.id); + start = Date.now(); + for (let index = 0; index < messages.length; index++) { + const message = messages.at(index); + // eslint-disable-next-line no-await-in-loop + await message.cleanup(); + } + window.log.info( + `removeAllMessagesInConversation messages.cleanup() ${conversationId} took ${Date.now() - + start}ms` + ); + start = Date.now(); + + // eslint-disable-next-line no-await-in-loop + await channels.removeMessagesByIds(ids); + window.log.info( + `removeAllMessagesInConversation: removeMessagesByIds ${conversationId} took ${Date.now() - + start}ms` + ); + } while (messages.length); - // eslint-disable-next-line no-await-in-loop await channels.removeAllMessagesInConversation(conversationId); window.log.info( - `removeAllMessagesInConversation: ${conversationId} took ${Date.now() - start}ms` + `removeAllMessagesInConversation: complete time ${conversationId} took ${Date.now() - + startFunction}ms` ); } diff --git a/ts/node/sql.ts b/ts/node/sql.ts index 6b227126e..47d40da65 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -958,7 +958,6 @@ function removeMessagesByIds(ids: Array, instance?: BetterSqlite3.Databa } const start = Date.now(); - // TODO we might need to do the same thing as assertGlobalInstanceOrInstance(instance) .prepare(`DELETE FROM ${MESSAGES_TABLE} WHERE id IN ( ${ids.map(() => '?').join(', ')} );`) .run(ids); @@ -974,11 +973,9 @@ function removeAllMessagesInConversation( } const inst = assertGlobalInstanceOrInstance(instance); - inst.transaction(() => { - inst - .prepare(`DELETE FROM ${MESSAGES_TABLE} WHERE conversationId = $conversationId`) - .run({ conversationId }); - })(); + inst + .prepare(`DELETE FROM ${MESSAGES_TABLE} WHERE conversationId = $conversationId`) + .run({ conversationId }); } function getMessageIdsFromServerIds(serverIds: Array, conversationId: string) { diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index f9d4245e2..c9c44fa9e 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -31,7 +31,7 @@ import { getIntl } from './user'; import { filter, isEmpty, isNumber, pick, sortBy } from 'lodash'; import { MessageReactsSelectorProps } from '../../components/conversation/message/message-content/MessageReactions'; -import { getSelectedConversation, getSelectedConversationKey } from './selectedConversation'; +import { getSelectedConversationKey } from './selectedConversation'; import { getModeratorsOutsideRedux } from './sogsRoomInfo'; export const getConversations = (state: StateType): ConversationsStateType => state.conversations; @@ -626,6 +626,17 @@ export const isFirstUnreadMessageIdAbove = createSelector( const getMessageId = (_whatever: any, id: string | undefined) => id; +/** + * A lot of our UI changes on the main panel need to happen quickly (composition box). + */ +export const getSelectedConversation = createSelector( + getConversationLookup, + getSelectedConversationKey, + (lookup, selectedConvo) => { + return selectedConvo ? lookup[selectedConvo] : undefined; + } +); + // tslint:disable: cyclomatic-complexity export const getMessagePropsByMessageId = createSelector( diff --git a/ts/state/selectors/selectedConversation.ts b/ts/state/selectors/selectedConversation.ts index a1e186d9c..31523151f 100644 --- a/ts/state/selectors/selectedConversation.ts +++ b/ts/state/selectors/selectedConversation.ts @@ -3,9 +3,9 @@ import { useSelector } from 'react-redux'; import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes'; import { PubKey } from '../../session/types'; import { UserUtils } from '../../session/utils'; -import { ReduxConversationType } from '../ducks/conversations'; import { StateType } from '../reducer'; import { getCanWrite, getModerators, getSubscriberCount } from './sogsRoomInfo'; +import { getSelectedConversation } from './conversations'; /** * Returns the formatted text for notification setting. @@ -58,11 +58,6 @@ export const getSelectedConversationKey = (state: StateType): string | undefined return state.conversations.selectedConversation; }; -export const getSelectedConversation = (state: StateType): ReduxConversationType | undefined => { - const selected = getSelectedConversationKey(state); - return selected ? state.conversations.conversationLookup[selected] : undefined; -}; - /** * Returns true if the current conversation selected is a public group and false otherwise. */ diff --git a/ts/state/smart/SessionConversation.ts b/ts/state/smart/SessionConversation.ts index c5850f2f1..6b40c2549 100644 --- a/ts/state/smart/SessionConversation.ts +++ b/ts/state/smart/SessionConversation.ts @@ -6,15 +6,13 @@ import { getHasOngoingCallWithFocusedConvo } from '../selectors/call'; import { getIsSelectedConvoInitialLoadingInProgress, getLightBoxOptions, + getSelectedConversation, getSelectedMessageIds, getSortedMessagesOfSelectedConversation, isMessageDetailView, isRightPanelShowing, } from '../selectors/conversations'; -import { - getSelectedConversation, - getSelectedConversationKey, -} from '../selectors/selectedConversation'; +import { getSelectedConversationKey } from '../selectors/selectedConversation'; import { getStagedAttachmentsForCurrentConversation } from '../selectors/stagedAttachments'; import { getTheme } from '../selectors/theme'; import { getOurNumber } from '../selectors/user';