From 554b445a3e2fce35195c4813b0ce71b198c45e1d Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 28 Mar 2023 14:44:41 +1100 Subject: [PATCH] feat: lookup for shared config message on link device --- ts/components/conversation/Timestamp.tsx | 27 +++++------ ts/models/conversation.ts | 4 +- ts/models/message.ts | 10 ++-- ts/node/sql.ts | 1 - ts/receiver/configMessage.ts | 48 ++++++++++++------- ts/session/apis/snode_api/swarmPolling.ts | 2 +- .../conversations/ConversationController.ts | 32 +++++++------ .../libsession_utils_convo_info_volatile.ts | 8 +++- ts/util/expiringMessages.ts | 2 +- ts/util/storage.ts | 1 + 10 files changed, 77 insertions(+), 58 deletions(-) diff --git a/ts/components/conversation/Timestamp.tsx b/ts/components/conversation/Timestamp.tsx index c89d447ad..87967d177 100644 --- a/ts/components/conversation/Timestamp.tsx +++ b/ts/components/conversation/Timestamp.tsx @@ -1,9 +1,10 @@ -import React, { useCallback, useState } from 'react'; +import React from 'react'; import moment from 'moment'; // tslint:disable-next-line: no-submodule-imports import useInterval from 'react-use/lib/useInterval'; import styled from 'styled-components'; +import useUpdate from 'react-use/lib/useUpdate'; type Props = { timestamp?: number; @@ -31,13 +32,7 @@ const TimestampContainerListItem = styled(TimestampContainerNotListItem)` `; export const Timestamp = (props: Props) => { - const [_lastUpdated, setLastUpdated] = useState(Date.now()); - // this is kind of a hack, but we use lastUpdated just to trigger a refresh. - // formatRelativeTime() will print the correct moment. - const update = useCallback(() => { - setLastUpdated(Date.now()); - }, [setLastUpdated]); - + const update = useUpdate(); useInterval(update, UPDATE_FREQUENCY); const { timestamp, momentFromNow } = props; @@ -47,14 +42,14 @@ export const Timestamp = (props: Props) => { } const momentValue = moment(timestamp); - let dateString: string = ''; - if (momentFromNow) { - dateString = momentValue.fromNow(); - } else { - dateString = momentValue.format('lll'); - } - - dateString = dateString.replace('minutes', 'mins').replace('minute', 'min'); + // this is a hack to make the date string shorter, looks like moment does not have a localized way of doing this for now. + + const dateString = momentFromNow + ? momentValue + .fromNow() + .replace('minutes', 'mins') + .replace('minute', 'min') + : momentValue.format('lll'); const title = moment(timestamp).format('llll'); if (props.isConversationListItem) { diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 615b0c7d9..a855c0b6c 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -1162,9 +1162,9 @@ export class ConversationModel extends Backbone.Model { } public async commit() { - perfStart(`conversationCommit-${this.attributes.id}`); + perfStart(`conversationCommit-${this.id}`); await commitConversationAndRefreshWrapper(this.id); - perfEnd(`conversationCommit-${this.attributes.id}`, 'conversationCommit'); + perfEnd(`conversationCommit-${this.id}`, 'conversationCommit'); } public async addSingleOutgoingMessage( diff --git a/ts/models/message.ts b/ts/models/message.ts index 2e88fbbfe..412af54c9 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -118,10 +118,10 @@ export class MessageModel extends Backbone.Model { const filledAttrs = fillMessageAttributesWithDefaults(attributes); super(filledAttrs); - if (!this.attributes.id) { + if (!this.id) { throw new Error('A message always needs to have an id.'); } - if (!this.attributes.conversationId) { + if (!this.get('conversationId')) { throw new Error('A message always needs to have an conversationId.'); } @@ -1084,17 +1084,17 @@ export class MessageModel extends Backbone.Model { } public async commit(triggerUIUpdate = true) { - if (!this.attributes.id) { + if (!this.id) { throw new Error('A message always needs an id'); } - perfStart(`messageCommit-${this.attributes.id}`); + perfStart(`messageCommit-${this.id}`); // because the saving to db calls _cleanData which mutates the field for cleaning, we need to save a copy const id = await Data.saveMessage(cloneDeep(this.attributes)); if (triggerUIUpdate) { this.dispatchMessageUpdate(); } - perfEnd(`messageCommit-${this.attributes.id}`, 'messageCommit'); + perfEnd(`messageCommit-${this.id}`, 'messageCommit'); return id; } diff --git a/ts/node/sql.ts b/ts/node/sql.ts index bc6cd9b23..5e95c1377 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -1368,7 +1368,6 @@ function getFirstUnreadMessageIdInConversation(conversationId: string) { } /** - * * Returns the last read message timestamp in the specific conversation (the columns `serverTimestamp` || `sent_at`) */ function getLastMessageReadInConversation(conversationId: string): number | null { diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index c2f4eae9b..739226bb1 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -1,4 +1,4 @@ -import _, { compact, isEmpty } from 'lodash'; +import _, { compact, isEmpty, toNumber } from 'lodash'; import { ConfigDumpData } from '../data/configDump/configDump'; import { Data, hasSyncedInitialConfigurationItem } from '../data/data'; import { ConversationInteraction } from '../interactions'; @@ -118,7 +118,9 @@ async function handleUserProfileUpdate(result: IncomingConfResult): Promise { try { - const start = Date.now(); + const startLoad = Date.now(); const convoModels = await Data.getAllConversations(); this.conversations.add(convoModels); - console.time('refreshAllWrapperContactsData'); + const start = Date.now(); // TODO make this a switch so we take care of all wrappers and have compilation errors if we forgot to add one. + // also keep in mind that the convo volatile one need to run for each convo. for (let index = 0; index < convoModels.length; index++) { const convo = convoModels[index]; if (SessionUtilContact.isContactToStoreInContactsWrapper(convo)) { await SessionUtilContact.refreshMappedValue(convo.id, true); - } - if (SessionUtilUserGroups.isUserGroupToStoreInWrapper(convo)) { + } else if (SessionUtilUserGroups.isUserGroupToStoreInWrapper(convo)) { await SessionUtilUserGroups.refreshCachedUserGroup(convo.id, true); } + // we actually want to run the convo volatile check and fetch even if one of the cases above is already true if (SessionUtilConvoInfoVolatile.isConvoToStoreInWrapper(convo)) { await SessionUtilConvoInfoVolatile.refreshConvoVolatileCached( convo.id, @@ -329,21 +330,22 @@ export class ConversationController { await convo.refreshInMemoryDetails(); } } - console.timeEnd('refreshAllWrapperContactsData'); + console.info(`refreshAllWrappersMappedValues took ${Date.now() - start}ms`); this._initialFetchComplete = true; - this.conversations.forEach((conversation: ConversationModel) => { - if ( - conversation.isActive() && - !conversation.isHidden() && - !conversation.get('lastMessage') - ) { - conversation.updateLastMessage(); - } - }); + // TODO do we really need to do this? + // this.conversations.forEach((conversation: ConversationModel) => { + // if ( + // conversation.isActive() && + // !conversation.isHidden() && + // !conversation.get('lastMessage') + // ) { + // conversation.updateLastMessage(); + // } + // }); window?.log?.info( - `ConversationController: done with initial fetch in ${Date.now() - start}ms.` + `ConversationController: done with initial fetch in ${Date.now() - startLoad}ms.` ); } catch (error) { window?.log?.error( diff --git a/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts b/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts index 8f840320c..ba3f8696b 100644 --- a/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts +++ b/ts/session/utils/libsession/libsession_utils_convo_info_volatile.ts @@ -65,6 +65,7 @@ function isConvoToStoreInWrapper(convo: ConversationModel): boolean { 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 ); } + function getConvoType(convo: ConversationModel): ConvoVolatileType { const convoType: ConvoVolatileType = SessionUtilContact.isContactToStoreInContactsWrapper(convo) || @@ -88,8 +89,13 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise< return; } const isForcedUnread = foundConvo.isMarkedUnread(); + const timestampFromDbMs = (await Data.fetchConvoMemoryDetails(convoId))?.lastReadTimestampMessage; + + // TODO not having a last read timestamp fallsback to 0, which keeps the existing value in the wrapper if it is already set (as done in src/convo_info_volatile_config.cpp) const lastReadMessageTimestamp = - (await Data.fetchConvoMemoryDetails(convoId))?.lastReadTimestampMessage || 0; + !!timestampFromDbMs && isFinite(timestampFromDbMs) && timestampFromDbMs > 0 + ? timestampFromDbMs + : 0; console.info( `convoInfoVolatile:insert "${convoId}";lastMessageReadTimestamp:${lastReadMessageTimestamp};forcedUnread:${isForcedUnread}...` diff --git a/ts/util/expiringMessages.ts b/ts/util/expiringMessages.ts index 31e1381d5..4bd10be5f 100644 --- a/ts/util/expiringMessages.ts +++ b/ts/util/expiringMessages.ts @@ -45,7 +45,7 @@ async function destroyExpiredMessages() { conversationKey: string; messageId: string; }> = messages.map(m => ({ - conversationKey: m.attributes.conversationId, + conversationKey: m.get('conversationId'), messageId: m.id, })); diff --git a/ts/util/storage.ts b/ts/util/storage.ts index 853325dc6..97e446871 100644 --- a/ts/util/storage.ts +++ b/ts/util/storage.ts @@ -126,6 +126,7 @@ export function getLastProfileUpdateTimestamp() { } export async function setLastProfileUpdateTimestamp(lastUpdateTimestamp: number) { + // TODO get rid of this once we remove the feature flag altogether if (window.sessionFeatureFlags.useSharedUtilForUserConfig) { return; }