Update sendStateByConversationId for lonely groups
This commit is contained in:
parent
d24da2e26d
commit
1e7d259909
3 changed files with 193 additions and 5 deletions
|
@ -210,6 +210,7 @@ export class BackupExportStream extends Readable {
|
|||
private readonly serviceIdToRecipientId = new Map<string, number>();
|
||||
private readonly e164ToRecipientId = new Map<string, number>();
|
||||
private readonly roomIdToRecipientId = new Map<string, number>();
|
||||
private ourConversation?: ConversationAttributesType;
|
||||
private attachmentBackupJobs: Array<CoreAttachmentBackupJobType> = [];
|
||||
private buffers = new Array<Uint8Array>();
|
||||
private nextRecipientId = 1;
|
||||
|
@ -256,6 +257,8 @@ export class BackupExportStream extends Readable {
|
|||
}
|
||||
|
||||
private async unsafeRun(backupLevel: BackupLevel): Promise<void> {
|
||||
this.ourConversation =
|
||||
window.ConversationController.getOurConversationOrThrow().attributes;
|
||||
this.push(
|
||||
Backups.BackupInfo.encodeDelimited({
|
||||
version: Long.fromNumber(BACKUP_VERSION),
|
||||
|
@ -1073,7 +1076,8 @@ export class BackupExportStream extends Readable {
|
|||
if (authorId === me) {
|
||||
result.outgoing = this.getOutgoingMessageDetails(
|
||||
message.sent_at,
|
||||
message
|
||||
message,
|
||||
{ conversationId: message.conversationId }
|
||||
);
|
||||
} else {
|
||||
result.incoming = this.getIncomingMessageDetails(message);
|
||||
|
@ -1229,7 +1233,8 @@ export class BackupExportStream extends Readable {
|
|||
if (isOutgoing) {
|
||||
result.outgoing = this.getOutgoingMessageDetails(
|
||||
message.sent_at,
|
||||
message
|
||||
message,
|
||||
{ conversationId: message.conversationId }
|
||||
);
|
||||
} else {
|
||||
result.incoming = this.getIncomingMessageDetails(message);
|
||||
|
@ -2345,7 +2350,8 @@ export class BackupExportStream extends Readable {
|
|||
}: Pick<
|
||||
MessageAttributesType,
|
||||
'sendStateByConversationId' | 'unidentifiedDeliveries' | 'errors'
|
||||
>
|
||||
>,
|
||||
{ conversationId }: { conversationId: string }
|
||||
): Backups.ChatItem.IOutgoingMessageDetails {
|
||||
const sealedSenderServiceIds = new Set(unidentifiedDeliveries);
|
||||
const errorMap = new Map(
|
||||
|
@ -2361,6 +2367,17 @@ export class BackupExportStream extends Readable {
|
|||
log.warn(`backups: no send target for a message ${sentAt}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter out our conversationId from non-"Note-to-Self" messages
|
||||
// TODO: DESKTOP-8089
|
||||
strictAssert(this.ourConversation?.id, 'our conversation must exist');
|
||||
if (
|
||||
id === this.ourConversation.id &&
|
||||
conversationId !== this.ourConversation.id
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { serviceId } = target.attributes;
|
||||
const recipientId = this.getOrPushPrivateRecipient(target.attributes);
|
||||
const timestamp =
|
||||
|
@ -2561,7 +2578,9 @@ export class BackupExportStream extends Readable {
|
|||
|
||||
// Directional details
|
||||
outgoing: isOutgoing
|
||||
? this.getOutgoingMessageDetails(history.timestamp, history)
|
||||
? this.getOutgoingMessageDetails(history.timestamp, history, {
|
||||
conversationId: message.conversationId,
|
||||
})
|
||||
: undefined,
|
||||
incoming: isOutgoing
|
||||
? undefined
|
||||
|
|
|
@ -1499,7 +1499,23 @@ export class BackupImportStream extends Writable {
|
|||
|
||||
const unidentifiedDeliveries = new Array<ServiceIdString>();
|
||||
const errors = new Array<CustomError>();
|
||||
for (const status of outgoing.sendStatus ?? []) {
|
||||
|
||||
let sendStatuses = outgoing.sendStatus;
|
||||
if (!sendStatuses?.length) {
|
||||
// TODO: DESKTOP-8089
|
||||
// If this outgoing message was not sent to anyone, we add ourselves to
|
||||
// sendStateByConversationId and mark read. This is to match existing desktop
|
||||
// behavior.
|
||||
sendStatuses = [
|
||||
{
|
||||
recipientId: item.authorId,
|
||||
read: new Backups.SendStatus.Read(),
|
||||
timestamp: item.dateSent,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
for (const status of sendStatuses) {
|
||||
strictAssert(
|
||||
status.recipientId,
|
||||
'sendStatus recipient must have an id'
|
||||
|
|
|
@ -22,6 +22,8 @@ import {
|
|||
OUR_ACI,
|
||||
} from './helpers';
|
||||
import { loadAllAndReinitializeRedux } from '../../services/allLoaders';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import type { MessageAttributesType } from '../../model-types';
|
||||
|
||||
const CONTACT_A = generateAci();
|
||||
const CONTACT_B = generateAci();
|
||||
|
@ -603,4 +605,155 @@ describe('backup/bubble messages', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
describe('lonely-in-group messages', async () => {
|
||||
const GROUP_ID = Bytes.toBase64(getRandomBytes(32));
|
||||
let group: ConversationModel | undefined;
|
||||
let ourConversation: ConversationModel | undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
group = await window.ConversationController.getOrCreateAndWait(
|
||||
GROUP_ID,
|
||||
'group',
|
||||
{
|
||||
groupVersion: 2,
|
||||
masterKey: Bytes.toBase64(getRandomBytes(32)),
|
||||
name: 'Rock Enthusiasts',
|
||||
active_at: 1,
|
||||
}
|
||||
);
|
||||
ourConversation = window.ConversationController.get(OUR_ACI);
|
||||
});
|
||||
|
||||
it('roundtrips messages that have our id in sendStateByConversationId', async () => {
|
||||
strictAssert(group, 'conversations exist');
|
||||
strictAssert(ourConversation, 'conversations exist');
|
||||
await symmetricRoundtripHarness([
|
||||
{
|
||||
conversationId: group.id,
|
||||
id: generateGuid(),
|
||||
type: 'outgoing',
|
||||
received_at: 3,
|
||||
received_at_ms: 3,
|
||||
sent_at: 3,
|
||||
timestamp: 3,
|
||||
sourceServiceId: OUR_ACI,
|
||||
body: 'd',
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Seen,
|
||||
sendStateByConversationId: {
|
||||
[ourConversation.id]: { status: SendStatus.Read, updatedAt: 3 },
|
||||
},
|
||||
expirationStartTimestamp: Date.now(),
|
||||
expireTimer: DurationInSeconds.fromMillis(WEEK),
|
||||
},
|
||||
]);
|
||||
});
|
||||
it(
|
||||
'if a message did not have sendStateByConversationId (e.g. to mimic post-import from primary), ' +
|
||||
'would add it with our conversationId when importing',
|
||||
async () => {
|
||||
strictAssert(group, 'conversations exist');
|
||||
strictAssert(ourConversation, 'conversations exist');
|
||||
const message: MessageAttributesType = {
|
||||
conversationId: group.id,
|
||||
id: generateGuid(),
|
||||
type: 'outgoing',
|
||||
received_at: 3,
|
||||
received_at_ms: 3,
|
||||
sent_at: 3,
|
||||
timestamp: 3,
|
||||
sourceServiceId: OUR_ACI,
|
||||
body: 'd',
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Seen,
|
||||
expirationStartTimestamp: Date.now(),
|
||||
expireTimer: DurationInSeconds.fromMillis(WEEK),
|
||||
};
|
||||
|
||||
await asymmetricRoundtripHarness(
|
||||
[
|
||||
{
|
||||
...message,
|
||||
sendStateByConversationId: {},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
...message,
|
||||
sendStateByConversationId: {
|
||||
[ourConversation.id]: { status: SendStatus.Read, updatedAt: 3 },
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
it('filters out our conversation id from sendStateByConversationId in non-note-to-self convos', async () => {
|
||||
strictAssert(group, 'conversations exist');
|
||||
strictAssert(ourConversation, 'conversations exist');
|
||||
const message: MessageAttributesType = {
|
||||
conversationId: group.id,
|
||||
id: generateGuid(),
|
||||
type: 'outgoing',
|
||||
received_at: 3,
|
||||
received_at_ms: 3,
|
||||
sent_at: 3,
|
||||
timestamp: 3,
|
||||
sourceServiceId: OUR_ACI,
|
||||
body: 'd',
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Seen,
|
||||
expirationStartTimestamp: Date.now(),
|
||||
expireTimer: DurationInSeconds.fromMillis(WEEK),
|
||||
};
|
||||
|
||||
await asymmetricRoundtripHarness(
|
||||
[
|
||||
{
|
||||
...message,
|
||||
sendStateByConversationId: {
|
||||
[ourConversation.id]: { status: SendStatus.Read, updatedAt: 3 },
|
||||
[contactA.id]: { status: SendStatus.Delivered, updatedAt: 4 },
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
...message,
|
||||
sendStateByConversationId: {
|
||||
[contactA.id]: { status: SendStatus.Delivered, updatedAt: 4 },
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
it('does not filter out our conversation id from sendStateByConversationId in Note-to-Self', async () => {
|
||||
strictAssert(ourConversation, 'conversations exist');
|
||||
const message: MessageAttributesType = {
|
||||
conversationId: ourConversation.id,
|
||||
id: generateGuid(),
|
||||
type: 'outgoing',
|
||||
received_at: 3,
|
||||
received_at_ms: 3,
|
||||
sent_at: 3,
|
||||
timestamp: 3,
|
||||
sourceServiceId: OUR_ACI,
|
||||
body: 'd',
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Seen,
|
||||
expirationStartTimestamp: Date.now(),
|
||||
expireTimer: DurationInSeconds.fromMillis(WEEK),
|
||||
};
|
||||
ourConversation.set({ active_at: 3 });
|
||||
|
||||
await symmetricRoundtripHarness([
|
||||
{
|
||||
...message,
|
||||
sendStateByConversationId: {
|
||||
[ourConversation.id]: { status: SendStatus.Read, updatedAt: 3 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue