fix: unread count and forced unread is synced

pull/2620/head
Audric Ackermann 2 years ago
parent c3a9d19882
commit c3e9d503e4

@ -3,6 +3,8 @@ const { Storage } = require('./ts/util/storage');
const url = require('url'); const url = require('url');
const _ = require('lodash');
const config = url.parse(window.location.toString(), true).query; const config = url.parse(window.location.toString(), true).query;
const configAny = config; const configAny = config;
@ -29,6 +31,7 @@ window.sessionFeatureFlags = {
useTestNet: Boolean( useTestNet: Boolean(
process.env.NODE_APP_INSTANCE && process.env.NODE_APP_INSTANCE.includes('testnet') process.env.NODE_APP_INSTANCE && process.env.NODE_APP_INSTANCE.includes('testnet')
), ),
useDebugLogging: !_.isEmpty(process.env.SESSION_DEBUG),
useClosedGroupV3: false || process.env.USE_CLOSED_GROUP_V3, useClosedGroupV3: false || process.env.USE_CLOSED_GROUP_V3,
useSharedUtilForUserConfig: true, useSharedUtilForUserConfig: true,
debug: { debug: {
@ -234,6 +237,7 @@ const { setupi18n } = require('./ts/util/i18n');
window.Signal = data.initData(); window.Signal = data.initData();
const { getConversationController } = require('./ts/session/conversations/ConversationController'); const { getConversationController } = require('./ts/session/conversations/ConversationController');
const { isEmpty } = require('lodash');
window.getConversationController = getConversationController; window.getConversationController = getConversationController;
// Linux seems to periodically let the event loop stop, so this is a global workaround // Linux seems to periodically let the event loop stop, so this is a global workaround
setInterval(() => { setInterval(() => {

@ -1,7 +1,7 @@
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { MessageDeliveryStatus } from '../../../../models/messageType'; import { LastMessageStatusType } from '../../../../state/ducks/conversations';
import { SessionIcon } from '../../../icon'; import { SessionIcon } from '../../../icon';
const MessageStatusSendingContainer = styled.div` const MessageStatusSendingContainer = styled.div`
@ -56,7 +56,7 @@ const MessageStatusError = ({ dataTestId }: { dataTestId?: string }) => {
}; };
export const OutgoingMessageStatus = (props: { export const OutgoingMessageStatus = (props: {
status?: MessageDeliveryStatus | null; status: LastMessageStatusType | null;
dataTestId?: string; dataTestId?: string;
}) => { }) => {
const { status, dataTestId } = props; const { status, dataTestId } = props;

@ -62,14 +62,14 @@ const ContactItem = (props: { contact: ContactPropsMessageDetail }) => {
const { contact } = props; const { contact } = props;
const errors = contact.errors || []; const errors = contact.errors || [];
const statusComponent = !contact.isOutgoingKeyError ? ( const statusComponent = (
<div <div
className={classNames( className={classNames(
'module-message-detail__contact__status-icon', 'module-message-detail__contact__status-icon',
`module-message-detail__contact__status-icon--${contact.status}` `module-message-detail__contact__status-icon--${contact.status}`
)} )}
/> />
) : null; );
return ( return (
<div key={contact.pubkey} className="module-message-detail__contact"> <div key={contact.pubkey} className="module-message-detail__contact">

@ -117,7 +117,6 @@ import {
type InMemoryConvoInfos = { type InMemoryConvoInfos = {
mentionedUs: boolean; mentionedUs: boolean;
unreadCount: number; unreadCount: number;
lastReadTimestampMessage: number | null;
}; };
// TODO decide it it makes sense to move this to a redux slice? // TODO decide it it makes sense to move this to a redux slice?
@ -275,7 +274,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
public getGroupAdmins(): Array<string> { public getGroupAdmins(): Array<string> {
const groupAdmins = this.get('groupAdmins'); const groupAdmins = this.get('groupAdmins');
return groupAdmins && groupAdmins?.length > 0 ? groupAdmins : []; return groupAdmins && groupAdmins.length > 0 ? groupAdmins : [];
} }
// tslint:disable-next-line: cyclomatic-complexity max-func-body-length // tslint:disable-next-line: cyclomatic-complexity max-func-body-length
@ -291,8 +290,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const weAreModerator = this.isModerator(ourNumber); // only used for sogs const weAreModerator = this.isModerator(ourNumber); // only used for sogs
const isMe = this.isMe(); const isMe = this.isMe();
const isTyping = !!this.typingTimer; const isTyping = !!this.typingTimer;
// const unreadCount = this.get('unreadCount') || undefined;
// const mentionedUs = this.get('mentionedUs') || undefined;
const isKickedFromGroup = !!this.get('isKickedFromGroup'); const isKickedFromGroup = !!this.get('isKickedFromGroup');
const left = !!this.get('left'); const left = !!this.get('left');
const currentNotificationSetting = this.get('triggerNotificationsFor'); const currentNotificationSetting = this.get('triggerNotificationsFor');
@ -337,7 +334,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
toRet.avatarPath = avatarPath; toRet.avatarPath = avatarPath;
} }
const foundContact = SessionUtilContact.getMappedValue(this.id); const foundContact = SessionUtilContact.getContactCached(this.id);
const foundCommunity = SessionUtilUserGroups.getCommunityByConvoIdCached(this.id); const foundCommunity = SessionUtilUserGroups.getCommunityByConvoIdCached(this.id);
const foundLegacyGroup = SessionUtilUserGroups.getLegacyGroupCached(this.id); const foundLegacyGroup = SessionUtilUserGroups.getLegacyGroupCached(this.id);
const foundVolatileInfo = SessionUtilConvoInfoVolatile.getVolatileInfoCached(this.id); const foundVolatileInfo = SessionUtilConvoInfoVolatile.getVolatileInfoCached(this.id);
@ -498,13 +495,10 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
inMemoryConvoInfos.delete(this.id); inMemoryConvoInfos.delete(this.id);
return; return;
} }
console.warn('memoryDetails', memoryDetails);
if (!inMemoryConvoInfos.get(this.id)) { if (!inMemoryConvoInfos.get(this.id)) {
inMemoryConvoInfos.set(this.id, { inMemoryConvoInfos.set(this.id, {
mentionedUs: false, mentionedUs: false,
unreadCount: 0, unreadCount: 0,
lastReadTimestampMessage: null,
}); });
} }
@ -517,10 +511,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
existing.unreadCount = memoryDetails.unreadCount; existing.unreadCount = memoryDetails.unreadCount;
changes = true; changes = true;
} }
if (existing.lastReadTimestampMessage !== memoryDetails.lastReadTimestampMessage) {
existing.lastReadTimestampMessage = memoryDetails.lastReadTimestampMessage;
changes = true;
}
if (existing.mentionedUs !== memoryDetails.mentionedUs) { if (existing.mentionedUs !== memoryDetails.mentionedUs) {
existing.mentionedUs = memoryDetails.mentionedUs; existing.mentionedUs = memoryDetails.mentionedUs;
changes = true; changes = true;
@ -531,10 +522,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
} }
} }
public getCachedLastReadTimestampMessage() {
return inMemoryConvoInfos.get(this.id)?.lastReadTimestampMessage || null;
}
public async queueJob(callback: () => Promise<void>) { public async queueJob(callback: () => Promise<void>) {
// tslint:disable-next-line: no-promise-as-boolean // tslint:disable-next-line: no-promise-as-boolean
const previous = this.pending || Promise.resolve(); const previous = this.pending || Promise.resolve();
@ -1855,8 +1842,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
return; return;
} }
const lastMessageModel = messages.at(0); const lastMessageModel = messages.at(0);
const lastMessageStatus = lastMessageModel?.getMessagePropStatus() || undefined; const lastMessageStatus = lastMessageModel.getMessagePropStatus() || undefined;
const lastMessageNotificationText = lastMessageModel?.getNotificationText() || undefined; const lastMessageNotificationText = lastMessageModel.getNotificationText() || undefined;
// we just want to set the `status` to `undefined` if there are no `lastMessageNotificationText` // we just want to set the `status` to `undefined` if there are no `lastMessageNotificationText`
const lastMessageUpdate = const lastMessageUpdate =
!!lastMessageNotificationText && !isEmpty(lastMessageNotificationText) !!lastMessageNotificationText && !isEmpty(lastMessageNotificationText)

@ -713,7 +713,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
// We include numbers we didn't successfully send to so we can display errors. // We include numbers we didn't successfully send to so we can display errors.
// Older messages don't have the recipients included on the message, so we fall // Older messages don't have the recipients included on the message, so we fall
// back to the conversation's current recipients // back to the conversation's current recipients
const phoneNumbers: Array<string> = this.isIncoming() const contacts: Array<string> = this.isIncoming()
? [this.get('source')] ? [this.get('source')]
: this.get('sent_to') || []; : this.get('sent_to') || [];
@ -727,30 +727,21 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
const errors = reject(allErrors, error => Boolean(error.number)); const errors = reject(allErrors, error => Boolean(error.number));
const errorsGroupedById = groupBy(allErrors, 'number'); const errorsGroupedById = groupBy(allErrors, 'number');
const finalContacts = await Promise.all( const finalContacts = await Promise.all(
(phoneNumbers || []).map(async id => { (contacts || []).map(async id => {
const errorsForContact = errorsGroupedById[id]; const errorsForContact = errorsGroupedById[id];
const isOutgoingKeyError = false;
const contact = this.findAndFormatContact(id); const contact = this.findAndFormatContact(id);
return { return {
...contact, ...contact,
// fallback to the message status if we do not have a status with a user status: this.getMessagePropStatus(),
// this is useful for medium groups.
status: this.getStatus(id) || this.getMessagePropStatus(),
errors: errorsForContact, errors: errorsForContact,
isOutgoingKeyError,
isPrimaryDevice: true,
profileName: contact.profileName, profileName: contact.profileName,
}; };
}) })
); );
// The prefix created here ensures that contacts with errors are listed // sort by pubkey
// first; otherwise it's alphabetical const sortedContacts = sortBy(finalContacts, contact => contact.pubkey);
const sortedContacts = sortBy(
finalContacts,
contact => `${contact.isPrimaryDevice ? '0' : '1'}${contact.pubkey}`
);
const toRet: MessagePropsDetails = { const toRet: MessagePropsDetails = {
sentAt: this.get('sent_at') || 0, sentAt: this.get('sent_at') || 0,
@ -1013,19 +1004,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
return lodashSize(this.get('errors')) > 0; return lodashSize(this.get('errors')) > 0;
} }
public getStatus(pubkey: string) {
const readBy = this.get('read_by') || [];
if (readBy.indexOf(pubkey) >= 0) {
return 'read';
}
const sentTo = this.get('sent_to') || [];
if (sentTo.indexOf(pubkey) >= 0) {
return 'sent';
}
return null;
}
public async updateMessageHash(messageHash: string) { public async updateMessageHash(messageHash: string) {
if (!messageHash) { if (!messageHash) {
window?.log?.error('Message hash not provided to update message hash'); window?.log?.error('Message hash not provided to update message hash');

@ -1,11 +1,14 @@
import { defaultsDeep } from 'lodash'; import { defaultsDeep } from 'lodash';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { CallNotificationType, PropsForMessageWithConvoProps } from '../state/ducks/conversations'; import {
CallNotificationType,
LastMessageStatusType,
PropsForMessageWithConvoProps,
} from '../state/ducks/conversations';
import { AttachmentTypeWithPath } from '../types/Attachment'; import { AttachmentTypeWithPath } from '../types/Attachment';
import { Reaction, ReactionList, SortedReactionList } from '../types/Reaction'; import { Reaction, ReactionList, SortedReactionList } from '../types/Reaction';
export type MessageModelType = 'incoming' | 'outgoing'; export type MessageModelType = 'incoming' | 'outgoing';
export type MessageDeliveryStatus = 'sending' | 'sent' | 'read' | 'error';
export interface MessageAttributes { export interface MessageAttributes {
// the id of the message // the id of the message
@ -48,7 +51,7 @@ export interface MessageAttributes {
* timestamp is the sent_at timestamp, which is the envelope.timestamp * timestamp is the sent_at timestamp, which is the envelope.timestamp
*/ */
timestamp?: number; timestamp?: number;
status?: MessageDeliveryStatus; status?: LastMessageStatusType;
sent_to: Array<string>; sent_to: Array<string>;
sent: boolean; sent: boolean;
@ -196,7 +199,7 @@ export interface MessageAttributesOptionals {
unread?: number; unread?: number;
group?: any; group?: any;
timestamp?: number; timestamp?: number;
status?: MessageDeliveryStatus; status?: LastMessageStatusType;
sent_to?: Array<string>; sent_to?: Array<string>;
sent?: boolean; sent?: boolean;
serverId?: number; serverId?: number;

@ -19,6 +19,7 @@ import {
CONVERSATIONS_TABLE, CONVERSATIONS_TABLE,
dropFtsAndTriggers, dropFtsAndTriggers,
GUARD_NODE_TABLE, GUARD_NODE_TABLE,
jsonToObject,
LAST_HASHES_TABLE, LAST_HASHES_TABLE,
MESSAGES_TABLE, MESSAGES_TABLE,
NODES_FOR_PUBKEY_TABLE, NODES_FOR_PUBKEY_TABLE,
@ -1486,11 +1487,16 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
* Setup up the User profile wrapper with what is stored in our own conversation * Setup up the User profile wrapper with what is stored in our own conversation
*/ */
const ourConversation = sqlNode.getConversationById(publicKeyHex, db); const ourConvoRow = db.prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`).get({
if (!ourConversation) { id: publicKeyHex,
});
if (!ourConvoRow) {
throw new Error('Failed to find our logged in conversation while migrating'); throw new Error('Failed to find our logged in conversation while migrating');
} }
const ourConversation = jsonToObject(ourConvoRow);
// Insert the user profile into the userWrappoer // Insert the user profile into the userWrappoer
const ourDbName = ourConversation.displayNameInProfile || ''; const ourDbName = ourConversation.displayNameInProfile || '';
const ourDbProfileUrl = ourConversation.avatarPointer || ''; const ourDbProfileUrl = ourConversation.avatarPointer || '';

@ -309,7 +309,7 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
const legacyGroupsToLeaveInDB = allLegacyGroupsInDb.filter(m => { const legacyGroupsToLeaveInDB = allLegacyGroupsInDb.filter(m => {
return !allLegacyGroupsIdsInWrapper.includes(m.id); return !allLegacyGroupsIdsInWrapper.includes(m.id);
}); });
// TODO we need to store the encryption keypair if needed
window.log.info( window.log.info(
`we have to join ${legacyGroupsToJoinInDB.length} legacy groups in DB compared to what is in the wrapper` `we have to join ${legacyGroupsToJoinInDB.length} legacy groups in DB compared to what is in the wrapper`
); );
@ -456,6 +456,39 @@ async function handleUserGroupsUpdate(result: IncomingConfResult): Promise<Incom
return result; return result;
} }
async function applyConvoVolatileUpdateFromWrapper(
convoId: string,
forcedUnread: boolean,
lastReadMessageTimestamp: number
) {
const foundConvo = getConversationController().get(convoId);
if (foundConvo) {
try {
window.log.debug(
`applyConvoVolatileUpdateFromWrapper: ${convoId}: forcedUnread:${forcedUnread}, lastReadMessage:${lastReadMessageTimestamp}`
);
// this should mark all the messages sent before fromWrapper.lastRead as read and update the unreadCount
await foundConvo.markReadFromConfigMessage(lastReadMessageTimestamp);
// this commits to the DB, if needed
await foundConvo.markAsUnread(forcedUnread, true);
if (SessionUtilConvoInfoVolatile.isConvoToStoreInWrapper(foundConvo)) {
await SessionUtilConvoInfoVolatile.refreshConvoVolatileCached(
foundConvo.id,
foundConvo.isClosedGroup(),
false
);
await foundConvo.refreshInMemoryDetails();
}
} catch (e) {
window.log.warn(
`applyConvoVolatileUpdateFromWrapper of "${convoId}" failed with error ${e.message}`
);
}
}
}
async function handleConvoInfoVolatileUpdate( async function handleConvoInfoVolatileUpdate(
result: IncomingConfResult result: IncomingConfResult
): Promise<IncomingConfResult> { ): Promise<IncomingConfResult> {
@ -463,50 +496,71 @@ async function handleConvoInfoVolatileUpdate(
// if (!result.needsDump) { // if (!result.needsDump) {
// return result; // return result;
// } // }
console.error('handleConvoInfoVolatileUpdate : TODO ');
const types = SessionUtilConvoInfoVolatile.getConvoInfoVolatileTypes(); const types = SessionUtilConvoInfoVolatile.getConvoInfoVolatileTypes();
for (let typeIndex = 0; typeIndex < types.length; typeIndex++) { for (let typeIndex = 0; typeIndex < types.length; typeIndex++) {
const type = types[typeIndex]; const type = types[typeIndex];
switch (type) { switch (type) {
case '1o1': case '1o1':
// Note: "Note to Self" comes here too try {
const privateChats = await ConvoInfoVolatileWrapperActions.getAll1o1(); // Note: "Note to Self" comes here too
for (let index = 0; index < privateChats.length; index++) { const wrapper1o1s = await ConvoInfoVolatileWrapperActions.getAll1o1();
const fromWrapper = privateChats[index]; for (let index = 0; index < wrapper1o1s.length; index++) {
const foundConvo = getConversationController().get(fromWrapper.pubkeyHex); const fromWrapper = wrapper1o1s[index];
// TODO should we create the conversation if the conversation does not exist locally? Or assume that it should be coming from a contacts update? await applyConvoVolatileUpdateFromWrapper(
if (foundConvo) { fromWrapper.pubkeyHex,
console.warn( fromWrapper.unread,
`fromWrapper from getAll1o1: ${fromWrapper.pubkeyHex}: ${fromWrapper.unread}` fromWrapper.lastRead
); );
// this should mark all the messages sent before fromWrapper.lastRead as read and update the unreadCount
await foundConvo.markReadFromConfigMessage(fromWrapper.lastRead);
// this commits to the DB, if needed
await foundConvo.markAsUnread(fromWrapper.unread, true);
if (SessionUtilConvoInfoVolatile.isConvoToStoreInWrapper(foundConvo)) {
await SessionUtilConvoInfoVolatile.refreshConvoVolatileCached(
foundConvo.id,
foundConvo.isClosedGroup(),
false
);
await foundConvo.refreshInMemoryDetails();
}
} }
} catch (e) {
window.log.warn('handleConvoInfoVolatileUpdate of "1o1" failed with error: ', e.message);
} }
console.warn('handleConvoInfoVolatileUpdate: privateChats', privateChats);
break; break;
case 'Community': case 'Community':
const comms = await ConvoInfoVolatileWrapperActions.getAllCommunities(); try {
console.warn('handleConvoInfoVolatileUpdate: comms', comms); const wrapperComms = await ConvoInfoVolatileWrapperActions.getAllCommunities();
for (let index = 0; index < wrapperComms.length; index++) {
const fromWrapper = wrapperComms[index];
const convoId = getOpenGroupV2ConversationId(
fromWrapper.baseUrl,
fromWrapper.roomCasePreserved
);
await applyConvoVolatileUpdateFromWrapper(
convoId,
fromWrapper.unread,
fromWrapper.lastRead
);
}
} catch (e) {
window.log.warn(
'handleConvoInfoVolatileUpdate of "Community" failed with error: ',
e.message
);
}
break; break;
case 'LegacyGroup': case 'LegacyGroup':
const legacyGroup = await ConvoInfoVolatileWrapperActions.getAllLegacyGroups(); try {
console.warn('handleConvoInfoVolatileUpdate: legacyGroup', legacyGroup); const legacyGroups = await ConvoInfoVolatileWrapperActions.getAllLegacyGroups();
for (let index = 0; index < legacyGroups.length; index++) {
const fromWrapper = legacyGroups[index];
await applyConvoVolatileUpdateFromWrapper(
fromWrapper.pubkeyHex,
fromWrapper.unread,
fromWrapper.lastRead
);
}
} catch (e) {
window.log.warn(
'handleConvoInfoVolatileUpdate of "LegacyGroup" failed with error: ',
e.message
);
}
break; break;
default: default:

@ -380,7 +380,6 @@ export const getRoomAndUpdateLastFetchTimestamp = async (
if (!newMessages.length) { if (!newMessages.length) {
// if we got no new messages, just write our last update timestamp to the db // if we got no new messages, just write our last update timestamp to the db
roomInfos.lastFetchTimestamp = Date.now(); roomInfos.lastFetchTimestamp = Date.now();
window?.log?.info();
await OpenGroupData.saveV2OpenGroupRoom(roomInfos); await OpenGroupData.saveV2OpenGroupRoom(roomInfos);
return null; return null;
} }

@ -221,26 +221,28 @@ export class ConversationController {
// open group v2 // open group v2
} else if (conversation.isPublic()) { } else if (conversation.isPublic()) {
window?.log?.info('leaving open group v2', conversation.id); window?.log?.info('leaving open group v2', conversation.id);
// remove from the wrapper the entries before we remove the roomInfos, as we won't have the required community pubkey afterwards
try {
await SessionUtilUserGroups.removeCommunityFromWrapper(conversation.id, conversation.id);
await SessionUtilConvoInfoVolatile.removeCommunityFromWrapper(
conversation.id,
conversation.id
);
} catch (e) {
window?.log?.info('SessionUtilUserGroups.removeCommunityFromWrapper failed:', e);
}
const roomInfos = OpenGroupData.getV2OpenGroupRoom(conversation.id); const roomInfos = OpenGroupData.getV2OpenGroupRoom(conversation.id);
if (roomInfos) { if (roomInfos) {
getOpenGroupManager().removeRoomFromPolledRooms(roomInfos); getOpenGroupManager().removeRoomFromPolledRooms(roomInfos);
} }
// remove the roomInfos locally for this open group room // remove the roomInfos locally for this open group room including the pubkey
try { try {
await OpenGroupData.removeV2OpenGroupRoom(conversation.id); await OpenGroupData.removeV2OpenGroupRoom(conversation.id);
} catch (e) { } catch (e) {
window?.log?.info('removeV2OpenGroupRoom failed:', e); window?.log?.info('removeV2OpenGroupRoom failed:', e);
} }
try {
await SessionUtilUserGroups.removeCommunityFromWrapper(conversation.id, conversation.id);
await SessionUtilConvoInfoVolatile.removeCommunityFromWrapper(
conversation.id,
conversation.id
);
} catch (e) {
window?.log?.info('SessionUtilUserGroups.removeCommunityFromWrapper failed:', e);
}
} else if (conversation.isPrivate()) { } else if (conversation.isPrivate()) {
// if this conversation is a private conversation it's in fact a `contact` for desktop. // if this conversation is a private conversation it's in fact a `contact` for desktop.
// we just want to remove everything related to it and set the hidden field to true // we just want to remove everything related to it and set the hidden field to true

@ -130,11 +130,7 @@ async function buildAndSaveDumpsToDB(changes: Array<SuccessfulChange>): Promise<
for (let i = 0; i < changes.length; i++) { for (let i = 0; i < changes.length; i++) {
const change = changes[i]; const change = changes[i];
const variant = LibSessionUtil.kindToVariant(change.message.kind); const variant = LibSessionUtil.kindToVariant(change.message.kind);
console.warn(
`ConfigurationSyncJob.saveDumpToDB: "${variant}" updatedHash: "${
change.updatedHash
}:${change.message.seqno.toNumber()}"`
);
const needsDump = await LibSessionUtil.markAsPushed( const needsDump = await LibSessionUtil.markAsPushed(
variant, variant,
change.publicKey, change.publicKey,
@ -302,11 +298,13 @@ async function queueNewJobIfNeeded() {
await runners.configurationSyncRunner.addJob( await runners.configurationSyncRunner.addJob(
new ConfigurationSyncJob({ nextAttemptTimestamp: Date.now() }) new ConfigurationSyncJob({ nextAttemptTimestamp: Date.now() })
); );
window.log.debug('Scheduling ConfSyncJob: ASAP');
} else { } else {
// if we did run at t=100, and it is currently t=110, diff is 10 // if we did run at t=100, and it is currently t=110, diff is 10
const diff = Math.max(Date.now() - lastRunConfigSyncJobTimestamp, 0); const diff = Math.max(Date.now() - lastRunConfigSyncJobTimestamp, 0);
// but we want to run every 30, so what we need is actually `30-10` from now = 20 // but we want to run every 30, so what we need is actually `30-10` from now = 20
const leftBeforeNextTick = Math.max(defaultMsBetweenRetries - diff, 0); const leftBeforeNextTick = Math.max(defaultMsBetweenRetries - diff, 0);
window.log.debug('Scheduling ConfSyncJob: LATER');
// TODO we need to make the addJob wait for the previous addJob to be done before it can be called. // TODO we need to make the addJob wait for the previous addJob to be done before it can be called.
await runners.configurationSyncRunner.addJob( await runners.configurationSyncRunner.addJob(

@ -112,6 +112,7 @@ async function pendingChangesForPubkey(pubkey: string): Promise<Array<OutgoingCo
const variant = dump.variant; const variant = dump.variant;
const needsPush = await GenericWrapperActions.needsPush(variant); const needsPush = await GenericWrapperActions.needsPush(variant);
if (!needsPush) { if (!needsPush) {
console.info('needsPush false for ', variant);
continue; continue;
} }

@ -126,7 +126,7 @@ function setMappedValue(info: ContactInfo) {
mappedContactWrapperValues.set(info.id, info); mappedContactWrapperValues.set(info.id, info);
} }
function getMappedValue(id: string) { function getContactCached(id: string) {
return mappedContactWrapperValues.get(id); return mappedContactWrapperValues.get(id);
} }
@ -134,6 +134,6 @@ export const SessionUtilContact = {
isContactToStoreInContactsWrapper, isContactToStoreInContactsWrapper,
insertAllContactsIntoContactsWrapper, insertAllContactsIntoContactsWrapper,
insertContactFromDBIntoWrapperAndRefresh, insertContactFromDBIntoWrapperAndRefresh,
getMappedValue, getContactCached,
refreshMappedValue, refreshMappedValue,
}; };

@ -1,4 +1,4 @@
import { uniq } from 'lodash'; import { isEmpty, uniq } from 'lodash';
import { BaseConvoInfoVolatile, ConvoVolatileType } from 'session_util_wrapper'; import { BaseConvoInfoVolatile, ConvoVolatileType } from 'session_util_wrapper';
import { Data } from '../../../data/data'; import { Data } from '../../../data/data';
import { OpenGroupData } from '../../../data/opengroups'; import { OpenGroupData } from '../../../data/opengroups';
@ -87,12 +87,12 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise<
if (!foundConvo || !isConvoToStoreInWrapper(foundConvo)) { if (!foundConvo || !isConvoToStoreInWrapper(foundConvo)) {
return; return;
} }
const isForcedUnread = foundConvo.isMarkedUnread(); const isForcedUnread = foundConvo.isMarkedUnread();
const lastReadTimestampMessage = foundConvo.getCachedLastReadTimestampMessage() || 0; const lastReadMessageTimestamp =
(await Data.fetchConvoMemoryDetails(convoId))?.lastReadTimestampMessage || 0;
console.info( console.info(
`convoInfoVolatile:insert "${convoId}";lastMessageReadTimestamp:${lastReadTimestampMessage};forcedUnread:${isForcedUnread}...` `convoInfoVolatile:insert "${convoId}";lastMessageReadTimestamp:${lastReadMessageTimestamp};forcedUnread:${isForcedUnread}...`
); );
const convoType = getConvoType(foundConvo); const convoType = getConvoType(foundConvo);
@ -102,7 +102,7 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise<
// this saves the details for contacts and `Note To Self` // this saves the details for contacts and `Note To Self`
await ConvoInfoVolatileWrapperActions.set1o1( await ConvoInfoVolatileWrapperActions.set1o1(
convoId, convoId,
lastReadTimestampMessage, lastReadMessageTimestamp,
isForcedUnread isForcedUnread
); );
await refreshConvoVolatileCached(convoId, false, false); await refreshConvoVolatileCached(convoId, false, false);
@ -116,7 +116,7 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise<
try { try {
await ConvoInfoVolatileWrapperActions.setLegacyGroup( await ConvoInfoVolatileWrapperActions.setLegacyGroup(
convoId, convoId,
lastReadTimestampMessage, lastReadMessageTimestamp,
isForcedUnread isForcedUnread
); );
await refreshConvoVolatileCached(convoId, true, false); await refreshConvoVolatileCached(convoId, true, false);
@ -124,14 +124,13 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise<
window.log.warn( window.log.warn(
`ConvoInfoVolatileWrapperActions.setLegacyGroup of ${convoId} failed with ${e.message}` `ConvoInfoVolatileWrapperActions.setLegacyGroup of ${convoId} failed with ${e.message}`
); );
// we stil let this go through
} }
break; break;
case 'Community': case 'Community':
try { try {
const asOpengroup = foundConvo.toOpenGroupV2(); const asOpengroup = foundConvo.toOpenGroupV2();
const roomDetails = OpenGroupData.getV2OpenGroupRoomByRoomId(asOpengroup); const roomDetails = OpenGroupData.getV2OpenGroupRoomByRoomId(asOpengroup);
if (!roomDetails) { if (!roomDetails || isEmpty(roomDetails.serverPublicKey)) {
return; return;
} }
@ -141,18 +140,19 @@ async function insertConvoFromDBIntoWrapperAndRefresh(convoId: string): Promise<
roomDetails.roomId, roomDetails.roomId,
roomDetails.serverPublicKey roomDetails.serverPublicKey
); );
// this does the create or the update of the matching existing community // this does the create or the update of the matching existing community
await ConvoInfoVolatileWrapperActions.setCommunityByFullUrl( await ConvoInfoVolatileWrapperActions.setCommunityByFullUrl(
fullUrlWithPubkey, fullUrlWithPubkey,
lastReadTimestampMessage, lastReadMessageTimestamp,
isForcedUnread isForcedUnread
); );
await refreshConvoVolatileCached(convoId, false, false); await refreshConvoVolatileCached(convoId, false, false);
} catch (e) { } catch (e) {
window.log.warn( window.log.warn(
`ConvoInfoVolatileWrapperActions.setCommunityByFullUrl of ${convoId} failed with ${e.message}` `ConvoInfoVolatileWrapperActions.setCommunityByFullUrl of ${convoId} failed with ${e.message}`
); );
// we still let this go through
} }
break; break;
default: default:
@ -173,29 +173,46 @@ async function refreshConvoVolatileCached(
duringAppStart: boolean duringAppStart: boolean
) { ) {
try { try {
let convoType: ConvoVolatileType = '1o1';
let refreshed = false; let refreshed = false;
if (OpenGroupUtils.isOpenGroupV2(convoId)) { if (OpenGroupUtils.isOpenGroupV2(convoId)) {
const fromWrapper = await ConvoInfoVolatileWrapperActions.getCommunity(convoId); convoType = 'Community';
if (fromWrapper && fromWrapper.fullUrlWithPubkey) {
mappedCommunityWrapperValues.set(convoId, fromWrapper);
}
refreshed = true;
} else if (convoId.startsWith('05') && isLegacyGroup) { } else if (convoId.startsWith('05') && isLegacyGroup) {
const fromWrapper = await ConvoInfoVolatileWrapperActions.getLegacyGroup(convoId); convoType = 'LegacyGroup';
if (fromWrapper) {
mappedLegacyGroupWrapperValues.set(convoId, fromWrapper);
}
refreshed = true;
} else if (convoId.startsWith('05')) { } else if (convoId.startsWith('05')) {
const fromWrapper = await ConvoInfoVolatileWrapperActions.get1o1(convoId); convoType = '1o1';
console.warn( }
`refreshConvoVolatileCached from get1o1 ${fromWrapper?.pubkeyHex} : ${fromWrapper?.unread}`
); switch (convoType) {
if (fromWrapper) { case '1o1':
mapped1o1WrapperValues.set(convoId, fromWrapper); const fromWrapper1o1 = await ConvoInfoVolatileWrapperActions.get1o1(convoId);
} if (fromWrapper1o1) {
refreshed = true; mapped1o1WrapperValues.set(convoId, fromWrapper1o1);
} // TODO handle the new closed groups once we got them ready }
refreshed = true;
break;
case 'LegacyGroup':
const fromWrapperLegacyGroup = await ConvoInfoVolatileWrapperActions.getLegacyGroup(
convoId
);
if (fromWrapperLegacyGroup) {
mappedLegacyGroupWrapperValues.set(convoId, fromWrapperLegacyGroup);
}
refreshed = true;
break;
case 'Community':
const fromWrapperCommunity = await ConvoInfoVolatileWrapperActions.getCommunity(convoId);
if (fromWrapperCommunity && fromWrapperCommunity.fullUrlWithPubkey) {
mappedCommunityWrapperValues.set(convoId, fromWrapperCommunity);
}
refreshed = true;
break;
default:
assertUnreachable(convoType, `refreshConvoVolatileCached unhandled case "${convoType}"`);
}
// TODO handle the new closed groups once we got them ready
if (refreshed && !duringAppStart) { if (refreshed && !duringAppStart) {
getConversationController() getConversationController()

@ -2,7 +2,6 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { Data } from '../../data/data'; import { Data } from '../../data/data';
import { import {
MessageDeliveryStatus,
MessageModelType, MessageModelType,
PropsForDataExtractionNotification, PropsForDataExtractionNotification,
PropsForMessageRequestResponse, PropsForMessageRequestResponse,
@ -45,8 +44,6 @@ export type ContactPropsMessageDetail = {
name?: string | null; name?: string | null;
profileName?: string | null; profileName?: string | null;
avatarPath?: string | null; avatarPath?: string | null;
isOutgoingKeyError: boolean;
errors?: Array<Error>; errors?: Array<Error>;
}; };
@ -60,7 +57,7 @@ export type MessagePropsDetails = {
direction: MessageModelType; direction: MessageModelType;
}; };
export type LastMessageStatusType = MessageDeliveryStatus | undefined; export type LastMessageStatusType = 'sending' | 'sent' | 'read' | 'error' | undefined;
export type FindAndFormatContactType = { export type FindAndFormatContactType = {
pubkey: string; pubkey: string;

@ -110,6 +110,9 @@ const development = window && window?.getEnvironment && window?.getEnvironment()
// The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api // The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api
function logAtLevel(level: string, prefix: string, ...args: any) { function logAtLevel(level: string, prefix: string, ...args: any) {
if (prefix === 'DEBUG' && window.sessionFeatureFlags.useDebugLogging) {
return;
}
if (development) { if (development) {
const fn = `_${level}`; const fn = `_${level}`;
(console as any)[fn](prefix, now(), ...args); (console as any)[fn](prefix, now(), ...args);

1
ts/window.d.ts vendored

@ -43,6 +43,7 @@ declare global {
debugNonSnodeRequests: boolean; debugNonSnodeRequests: boolean;
debugOnionRequests: boolean; debugOnionRequests: boolean;
}; };
useDebugLogging: boolean;
}; };
SessionSnodeAPI: SessionSnodeAPI; SessionSnodeAPI: SessionSnodeAPI;
onLogin: (pw: string) => Promise<void>; onLogin: (pw: string) => Promise<void>;

Loading…
Cancel
Save