fix: mark messages sent before our lastreadts from the wrapper as read

pull/2620/head
Audric Ackermann 2 years ago
parent 328c131d8b
commit 318a77be4b

@ -98,7 +98,7 @@
"glob": "7.1.2",
"image-type": "^4.1.0",
"ip2country": "1.0.1",
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.1.8/libsession_util_nodejs-v0.1.8.tar.gz",
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.1.10/libsession_util_nodejs-v0.1.10.tar.gz",
"libsodium-wrappers-sumo": "^0.7.9",
"linkify-it": "3.0.2",
"lodash": "^4.17.20",

@ -38,12 +38,12 @@ import { ActionPanelOnionStatusLight } from '../dialog/OnionStatusPathDialog';
import { SessionIconButton } from '../icon';
import { LeftPaneSectionContainer } from './LeftPaneSectionContainer';
import { SettingsKey } from '../../data/settings-key';
import { getLatestReleaseFromFileServer } from '../../session/apis/file_server_api/FileServerApi';
import { forceRefreshRandomSnodePool } from '../../session/apis/snode_api/snodePool';
import { isDarkTheme } from '../../state/selectors/theme';
import { ThemeStateType } from '../../themes/constants/colors';
import { switchThemeTo } from '../../themes/switchTheme';
import { SettingsKey } from '../../data/settings-key';
const Section = (props: { type: SectionType }) => {
const ourNumber = useSelector(getOurNumber);

@ -103,6 +103,7 @@ import {
fillConvoAttributesWithDefaults,
isDirectConversation,
isOpenOrClosedGroup,
READ_MESSAGE_STATE,
} from './conversationAttributes';
import { LibSessionUtil } from '../session/utils/libsession/libsession_utils';
@ -113,6 +114,7 @@ import {
getModeratorsOutsideRedux,
getSubscriberCountOutsideRedux,
} from '../state/selectors/sogsRoomInfo';
import { markAttributesAsReadIfNeeded } from './messageFactory';
type InMemoryConvoInfos = {
mentionedUs: boolean;
@ -345,10 +347,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
if (this.get('expireTimer')) {
toRet.expireTimer = this.get('expireTimer');
}
if (this.get('markedAsUnread')) {
toRet.isMarkedUnread = this.get('markedAsUnread');
}
// those are values coming only from both the DB or the wrapper. Currently we display the data from the DB
if (this.isClosedGroup()) {
toRet.members = this.get('members') || [];
@ -682,7 +680,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
messageRequestResponse: {
isApproved: 1,
},
unread: 1, // 1 means unread
unread: READ_MESSAGE_STATE.unread, // 1 means unread
expireTimer: 0,
});
this.updateLastMessage();
@ -829,7 +827,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
...commonAttributes,
// Even though this isn't reflected to the user, we want to place the last seen
// indicator above it. We set it to 'unread' to trigger that placement.
unread: 1,
unread: READ_MESSAGE_STATE.unread,
source,
sent_at: timestamp,
received_at: timestamp,
@ -931,7 +929,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
source: sender,
type: 'outgoing',
direction: 'outgoing',
unread: 0, // an outgoing message must be already read
unread: READ_MESSAGE_STATE.read, // an outgoing message must be already read
received_at: messageAttributes.sent_at, // make sure to set a received_at timestamp for an outgoing message, so the order are right.
});
}
@ -944,12 +942,18 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
await this.setDidApproveMe(true);
}
return this.addSingleMessage({
const toBeAddedAttributes: MessageAttributesOptionals = {
...messageAttributes,
conversationId: this.id,
type: 'incoming',
direction: 'outgoing',
});
};
// if the message is trying to be added unread, make sure that it shouldn't be already read from our other devices
markAttributesAsReadIfNeeded(toBeAddedAttributes);
return this.addSingleMessage(toBeAddedAttributes);
}
/**

@ -153,3 +153,8 @@ export const CONVERSATION_PRIORITIES = {
hidden: -1,
pinned: 1, // anything over 0 means pinned, but when our local users pins a conversation, we set the priority to 1
};
export const READ_MESSAGE_STATE = {
unread: 1,
read: 0,
} as const;

@ -94,6 +94,7 @@ import { Notifications } from '../util/notifications';
import { Storage } from '../util/storage';
import { ConversationModel } from './conversation';
import { roomHasBlindEnabled } from '../types/sqlSharedTypes';
import { READ_MESSAGE_STATE } from './conversationAttributes';
// tslint:disable: cyclomatic-complexity
/**
@ -205,7 +206,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
const { unread } = attributes;
if (unread === undefined) {
this.set({ unread: 0 });
this.set({ unread: READ_MESSAGE_STATE.read });
}
this.set(attributes);
@ -1100,7 +1101,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
}
public markMessageReadNoCommit(readAt: number) {
this.set({ unread: 0 });
this.set({ unread: READ_MESSAGE_STATE.read });
if (
this.get('expireTimer') &&

@ -1,4 +1,6 @@
import { UserUtils } from '../session/utils';
import { SessionUtilConvoInfoVolatile } from '../session/utils/libsession/libsession_utils_convo_info_volatile';
import { READ_MESSAGE_STATE } from './conversationAttributes';
import { MessageModel } from './message';
import { MessageAttributesOptionals, MessageModelType } from './messageType';
@ -86,7 +88,7 @@ function getSharedAttributesForPublicMessage({
function getSharedAttributesForOutgoingMessage() {
return {
source: UserUtils.getOurPubKeyStrFromCache(),
unread: 0,
unread: READ_MESSAGE_STATE.read,
sent_to: [],
sent: true,
type: 'outgoing' as MessageModelType,
@ -96,12 +98,29 @@ function getSharedAttributesForOutgoingMessage() {
function getSharedAttributesForIncomingMessage() {
return {
unread: 1,
unread: READ_MESSAGE_STATE.unread,
type: 'incoming' as MessageModelType,
direction: 'incoming' as MessageModelType,
};
}
export function markAttributesAsReadIfNeeded(messageAttributes: MessageAttributesOptionals) {
// if the message is trying to be added unread, make sure that it shouldn't be already read from our other devices
if (messageAttributes.unread === READ_MESSAGE_STATE.unread) {
const latestUnreadForThisConvo = SessionUtilConvoInfoVolatile.getVolatileInfoCached(
messageAttributes.conversationId
);
const sentAt = messageAttributes.serverTimestamp || messageAttributes.sent_at;
if (
sentAt &&
latestUnreadForThisConvo?.lastRead &&
sentAt <= latestUnreadForThisConvo.lastRead
) {
messageAttributes.unread = READ_MESSAGE_STATE.read;
}
}
}
/**
* This function is only called when we get a message from ourself from an opengroup polling event
*/
@ -128,11 +147,12 @@ export function createPublicMessageSentFromNotUs(args: {
serverTimestamp: number;
conversationId: string;
}): MessageModel {
const messageData: MessageAttributesOptionals = {
const messageAttributes: MessageAttributesOptionals = {
...getSharedAttributesForPublicMessage(args),
...getSharedAttributesForIncomingMessage(),
source: args.sender,
};
markAttributesAsReadIfNeeded(messageAttributes);
return new MessageModel(messageData);
return new MessageModel(messageAttributes);
}

@ -7,6 +7,7 @@ import {
} from '../state/ducks/conversations';
import { AttachmentTypeWithPath } from '../types/Attachment';
import { Reaction, ReactionList, SortedReactionList } from '../types/Reaction';
import { READ_MESSAGE_STATE } from './conversationAttributes';
export type MessageModelType = 'incoming' | 'outgoing';
@ -44,6 +45,7 @@ export interface MessageAttributes {
};
/**
* 1 means unread, 0 or anything else is read.
* You can use the values from READ_MESSAGE_STATE.unread and READ_MESSAGE_STATE.read
*/
unread: number;
group?: any;
@ -224,7 +226,7 @@ export const fillMessageAttributesWithDefaults = (
const defaulted = defaultsDeep(optAttributes, {
expireTimer: 0, // disabled
id: uuidv4(),
unread: 0, // if nothing is set, this message is considered read
unread: READ_MESSAGE_STATE.read, // if nothing is set, this message is considered read
});
// this is just to cleanup a bit the db. delivered and delivered_to were removed, so everytime we load a message
// we make sure to clean those fields in the json.

@ -1,6 +1,7 @@
import { compact, isEmpty, isNumber, toNumber } from 'lodash';
import { ConfigDumpData } from '../data/configDump/configDump';
import { Data } from '../data/data';
import { SettingsKey } from '../data/settings-key';
import { ConversationInteraction } from '../interactions';
import { ConversationTypeEnum } from '../models/conversationAttributes';
import { SignalService } from '../protobuf';
@ -27,6 +28,7 @@ import { configurationMessageReceived, trigger } from '../shims/events';
import { assertUnreachable } from '../types/sqlSharedTypes';
import { BlockedNumberController } from '../util';
import { Registration } from '../util/registration';
import { ReleasedFeatures } from '../util/releaseFeature';
import {
Storage,
getLastProfileUpdateTimestamp,
@ -45,8 +47,6 @@ import { addKeyPairToCacheAndDBIfNeeded, handleNewClosedGroup } from './closedGr
import { HexKeyPair } from './keypairs';
import { queueAllCachedFromSource } from './receiver';
import { EnvelopePlus } from './types';
import { SettingsKey } from '../data/settings-key';
import { ReleasedFeatures } from '../util/releaseFeature';
function groupByVariant(
incomingConfigs: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
@ -92,13 +92,14 @@ async function mergeConfigsWithIncomingUpdates(
data: msg.message.data,
hash: msg.messageHash,
}));
await GenericWrapperActions.merge(variant, toMerge);
const mergedCount = await GenericWrapperActions.merge(variant, toMerge);
const needsPush = await GenericWrapperActions.needsPush(variant);
const needsDump = await GenericWrapperActions.needsDump(variant);
const latestEnvelopeTimestamp = Math.max(...sameVariant.map(m => m.envelopeTimestamp));
window.log.debug(`${variant}: "${publicKey}" needsPush:${needsPush} needsDump:${needsDump} `);
window.log.debug(
`${variant}: "${publicKey}" needsPush:${needsPush} needsDump:${needsDump}; mergedCount:${mergedCount} `
);
const incomingConfResult: IncomingConfResult = {
needsDump,
@ -118,9 +119,6 @@ async function mergeConfigsWithIncomingUpdates(
}
async function handleUserProfileUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {
if (!result.needsDump) {
return result;
}
const updateUserInfo = await UserConfigWrapperActions.getUserInfo();
if (!updateUserInfo) {
return result;
@ -140,9 +138,6 @@ async function handleUserProfileUpdate(result: IncomingConfResult): Promise<Inco
// tslint:disable-next-line: cyclomatic-complexity
async function handleContactsUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {
if (!result.needsDump) {
return result;
}
const us = UserUtils.getOurPubKeyStrFromCache();
const allContacts = await ContactsWrapperActions.getAll();
@ -432,10 +427,6 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
}
async function handleUserGroupsUpdate(result: IncomingConfResult): Promise<IncomingConfResult> {
if (!result.needsDump) {
return result;
}
const toHandle = SessionUtilUserGroups.getUserGroupTypes();
for (let index = 0; index < toHandle.length; index++) {
const typeToHandle = toHandle[index];
@ -494,10 +485,6 @@ async function applyConvoVolatileUpdateFromWrapper(
async function handleConvoInfoVolatileUpdate(
result: IncomingConfResult
): Promise<IncomingConfResult> {
if (!result.needsDump) {
return result;
}
const types = SessionUtilConvoInfoVolatile.getConvoInfoVolatileTypes();
for (let typeIndex = 0; typeIndex < types.length; typeIndex++) {
const type = types[typeIndex];
@ -508,6 +495,7 @@ async function handleConvoInfoVolatileUpdate(
const wrapper1o1s = await ConvoInfoVolatileWrapperActions.getAll1o1();
for (let index = 0; index < wrapper1o1s.length; index++) {
const fromWrapper = wrapper1o1s[index];
await applyConvoVolatileUpdateFromWrapper(
fromWrapper.pubkeyHex,
fromWrapper.unread,
@ -650,8 +638,12 @@ async function handleConfigMessagesViaLibSession(
return;
}
window?.log?.info(
`Handling our sharedConfig message via libsession_util; count: ${configMessages.length}`
window?.log?.debug(
`Handling our sharedConfig message via libsession_util ${configMessages.map(m => ({
variant: LibSessionUtil.kindToVariant(m.message.kind),
hash: m.messageHash,
seqno: (m.message.seqno as Long).toNumber(),
}))}`
);
const incomingMergeResult = await mergeConfigsWithIncomingUpdates(configMessages);

@ -12,7 +12,7 @@ import {
deleteMessagesFromSwarmAndCompletelyLocally,
deleteMessagesFromSwarmAndMarkAsDeletedLocally,
} from '../interactions/conversations/unsendingInteractions';
import { ConversationTypeEnum } from '../models/conversationAttributes';
import { ConversationTypeEnum, READ_MESSAGE_STATE } from '../models/conversationAttributes';
import { findCachedBlindedMatchOrLookupOnAllServers } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import { getConversationController } from '../session/conversations';
import { concatUInt8Array, getSodiumRenderer } from '../session/crypto';
@ -761,7 +761,7 @@ export async function handleDataExtractionNotification(
referencedAttachmentTimestamp, // currently unused
source,
},
unread: 1, // 1 means unread
unread: READ_MESSAGE_STATE.unread, // 1 means unread
expireTimer: 0,
});
convo.updateLastMessage();

@ -7,7 +7,7 @@ import { MessageModel, sliceQuoteText } from '../models/message';
import { getConversationController } from '../session/conversations';
import { Quote } from './types';
import { ConversationTypeEnum } from '../models/conversationAttributes';
import { ConversationTypeEnum, READ_MESSAGE_STATE } from '../models/conversationAttributes';
import { MessageDirection } from '../models/messageType';
import { SignalService } from '../protobuf';
import { ProfileManager } from '../session/profile_manager/ProfileManager';
@ -144,7 +144,7 @@ async function processProfileKeyNoCommit(
function updateReadStatus(message: MessageModel) {
if (message.isExpirationTimerUpdate()) {
message.set({ unread: 0 });
message.set({ unread: READ_MESSAGE_STATE.read });
}
}
@ -307,7 +307,7 @@ async function handleExpirationTimerUpdateNoCommit(
source,
expireTimer,
},
unread: 0, // mark the message as read.
unread: READ_MESSAGE_STATE.read, // mark the message as read.
});
conversation.set({ expireTimer });

@ -1,4 +1,4 @@
import _ from 'lodash';
import _, { shuffle } from 'lodash';
import { Data, Snode } from '../../../data/data';
@ -312,10 +312,11 @@ export async function getSwarmFor(pubkey: string): Promise<Array<Snode>> {
}
// Request new node list from the network
const freshNodes = _.shuffle(await requestSnodesForPubkeyFromNetwork(pubkey));
const swarm = await requestSnodesForPubkeyFromNetwork(pubkey);
const mixedSwarm = shuffle(swarm);
const edkeys = freshNodes.map((n: Snode) => n.pubkey_ed25519);
const edkeys = mixedSwarm.map((n: Snode) => n.pubkey_ed25519);
await internalUpdateSwarmFor(pubkey, edkeys);
return freshNodes;
return mixedSwarm;
}

@ -251,7 +251,7 @@ export class SwarmPolling {
if (!isGroup && userConfigMessagesMerged.length) {
window.log.info(
`received userConfigMessagesMerged: ${userConfigMessagesMerged.length} for key ${pubkey.key}`
`received userConfigMessages count: ${userConfigMessagesMerged.length} for key ${pubkey.key}`
);
try {
await this.handleSharedConfigMessages(userConfigMessagesMerged);

@ -29,6 +29,7 @@ import { PnServer } from '../../apis/push_notification_api';
import { approveConvoAndSendResponse } from '../../../interactions/conversationInteractions';
import { GetNetworkTime } from '../../apis/snode_api/getNetworkTime';
import { SnodeNamespaces } from '../../apis/snode_api/namespaces';
import { READ_MESSAGE_STATE } from '../../../models/conversationAttributes';
// tslint:disable: function-name
@ -860,7 +861,7 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) {
received_at: networkTimestamp,
expireTimer: 0,
callNotificationType: 'answered-a-call',
unread: 0,
unread: READ_MESSAGE_STATE.read,
});
await buildAnswerAndSendIt(fromSender);
@ -1199,7 +1200,7 @@ async function addMissedCallMessage(callerPubkey: string, sentAt: number) {
received_at: GetNetworkTime.getNowWithNetworkOffset(),
expireTimer: 0,
callNotificationType: 'missed-call',
unread: 1,
unread: READ_MESSAGE_STATE.unread,
});
}

@ -126,6 +126,7 @@ async function pendingChangesForPubkey(pubkey: string): Promise<Array<OutgoingCo
const { data, seqno, hashes } = await GenericWrapperActions.push(variant);
const kind = variantToKind(variant);
const namespace = await GenericWrapperActions.storageNamespace(variant);
results.push({
message: new SharedConfigMessage({

@ -5148,9 +5148,9 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.1.8/libsession_util_nodejs-v0.1.8.tar.gz":
version "0.1.8"
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.1.8/libsession_util_nodejs-v0.1.8.tar.gz#49a296dc1d81db5ec8104ef1e6ce5ed2cecff6e9"
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.1.10/libsession_util_nodejs-v0.1.10.tar.gz":
version "0.1.10"
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.1.10/libsession_util_nodejs-v0.1.10.tar.gz#d508007fd15976bdb8b168234d7a7a2e54c7eaf8"
dependencies:
cmake-js "^7.2.1"
node-addon-api "^6.1.0"

Loading…
Cancel
Save