feat: lookup for shared config message on link device

pull/2620/head
Audric Ackermann 2 years ago
parent 0050352470
commit 554b445a3e

@ -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) {

@ -1162,9 +1162,9 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
}
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(

@ -118,10 +118,10 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
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<MessageAttributes> {
}
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;
}

@ -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 {

@ -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<Inco
const updatedProfilePicture = await UserConfigWrapperActions.getProfilePicture();
const picUpdate = !isEmpty(updatedProfilePicture.key) && !isEmpty(updatedProfilePicture.url);
await ProfileManager.updateOurProfileSync(
await updateOurProfileLegacyOrViaLibSession(
result.latestEnvelopeTimestamp,
updatedUserName,
picUpdate ? updatedProfilePicture.url : null,
picUpdate ? updatedProfilePicture.key : null
@ -156,8 +158,8 @@ async function handleContactsUpdate(result: IncomingConfResult): Promise<Incomin
changes = true;
}
if (!wrapperConvo.hidden && !contactConvo.isHidden()) {
contactConvo.set({ hidden: false });
if (Boolean(wrapperConvo.hidden) !== Boolean(contactConvo.isHidden())) {
contactConvo.set({ hidden: !!wrapperConvo.hidden });
changes = true;
}
@ -635,6 +637,23 @@ async function handleConfigMessagesViaLibSession(
await processMergingResults(incomingMergeResult);
}
async function updateOurProfileLegacyOrViaLibSession(
sentAt: number,
displayName: string,
profileUrl: string | null,
profileKey: Uint8Array | null
) {
await ProfileManager.updateOurProfileSync(displayName, profileUrl, profileKey);
await setLastProfileUpdateTimestamp(toNumber(sentAt));
// do not trigger a signin by linking if the display name is empty
if (!isEmpty(displayName)) {
trigger(configurationMessageReceived, displayName);
} else {
window?.log?.warn('Got a configuration message but the display name is empty');
}
}
async function handleOurProfileUpdateLegacy(
sentAt: number | Long,
configMessage: SignalService.ConfigurationMessage
@ -650,15 +669,12 @@ async function handleOurProfileUpdateLegacy(
);
const { profileKey, profilePicture, displayName } = configMessage;
await ProfileManager.updateOurProfileSync(displayName, profilePicture, profileKey);
await setLastProfileUpdateTimestamp(_.toNumber(sentAt));
// do not trigger a signin by linking if the display name is empty
if (displayName) {
trigger(configurationMessageReceived, displayName);
} else {
window?.log?.warn('Got a configuration message but the display name is empty');
}
await updateOurProfileLegacyOrViaLibSession(
toNumber(sentAt),
displayName,
profilePicture,
profileKey
);
}
}
@ -669,7 +685,7 @@ async function handleGroupsAndContactsFromConfigMessageLegacy(
if (window.sessionFeatureFlags.useSharedUtilForUserConfig) {
return;
}
const envelopeTimestamp = _.toNumber(envelope.timestamp);
const envelopeTimestamp = toNumber(envelope.timestamp);
const lastConfigUpdate = await Data.getItemById(hasSyncedInitialConfigurationItem);
const lastConfigTimestamp = lastConfigUpdate?.timestamp;
const isNewerConfig =
@ -789,14 +805,14 @@ const handleContactFromConfigLegacy = async (
const existingActiveAt = contactConvo.get('active_at');
if (!existingActiveAt || existingActiveAt === 0) {
contactConvo.set('active_at', _.toNumber(envelope.timestamp));
contactConvo.set('active_at', toNumber(envelope.timestamp));
}
// checking for existence of field on protobuf
if (contactReceived.isApproved === true) {
if (!contactConvo.isApproved()) {
await contactConvo.setIsApproved(Boolean(contactReceived.isApproved));
await contactConvo.addOutgoingApprovalMessage(_.toNumber(envelope.timestamp));
await contactConvo.addOutgoingApprovalMessage(toNumber(envelope.timestamp));
}
if (contactReceived.didApproveMe === true) {

@ -145,7 +145,7 @@ export class SwarmPolling {
public async pollForAllKeys() {
if (!window.getGlobalOnlineStatus()) {
window?.log?.error('pollForAllKeys: offline');
// Important to set up a new polling
// Very important to set up a new polling call so we do retry at some point
setTimeout(this.pollForAllKeys.bind(this), SWARM_POLLING_TIMEOUT.ACTIVE);
return;
}

@ -305,20 +305,21 @@ export class ConversationController {
const load = async () => {
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(

@ -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}...`

@ -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,
}));

@ -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;
}

Loading…
Cancel
Save