Clean up group story replies

This commit is contained in:
Josh Perez 2022-11-01 14:58:07 -04:00 committed by GitHub
parent 50c48315e3
commit 6700f6fa15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 28 deletions

View file

@ -3938,9 +3938,20 @@ export class ConversationModel extends window.Backbone
'desktop.mandatoryProfileSharing' 'desktop.mandatoryProfileSharing'
); );
await this.maybeApplyUniversalTimer(); let expirationStartTimestamp: number | undefined;
let expireTimer: number | undefined;
const expireTimer = this.get('expireTimer'); // If it's a group story reply then let's match the expiration timers
// with the parent story's expiration.
if (storyId && isGroup(this.attributes)) {
const parentStory = await getMessageById(storyId);
expirationStartTimestamp =
parentStory?.expirationStartTimestamp || Date.now();
expireTimer = parentStory?.expireTimer || durations.DAY;
} else {
await this.maybeApplyUniversalTimer();
expireTimer = this.get('expireTimer');
}
const recipientMaybeConversations = map( const recipientMaybeConversations = map(
this.getRecipients({ this.getRecipients({
@ -3983,6 +3994,7 @@ export class ConversationModel extends window.Backbone
sent_at: now, sent_at: now,
received_at: window.Signal.Util.incrementMessageCounter(), received_at: window.Signal.Util.incrementMessageCounter(),
received_at_ms: now, received_at_ms: now,
expirationStartTimestamp,
expireTimer, expireTimer,
readStatus: ReadStatus.Read, readStatus: ReadStatus.Read,
seenStatus: SeenStatus.NotApplicable, seenStatus: SeenStatus.NotApplicable,

View file

@ -291,6 +291,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
INITIAL_PROTOCOL_VERSION?: number; INITIAL_PROTOCOL_VERSION?: number;
deletingForEveryone?: boolean;
isSelected?: boolean; isSelected?: boolean;
private pendingMarkRead?: number; private pendingMarkRead?: number;
@ -1694,9 +1696,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
attributesToUpdate.sendStateByConversationId = sendStateByConversationId; attributesToUpdate.sendStateByConversationId = sendStateByConversationId;
attributesToUpdate.expirationStartTimestamp = sentToAtLeastOneRecipient // Only update the expirationStartTimestamp if we don't already have one set
? Date.now() if (!this.get('expirationStartTimestamp')) {
: undefined; attributesToUpdate.expirationStartTimestamp = sentToAtLeastOneRecipient
? Date.now()
: undefined;
}
attributesToUpdate.unidentifiedDeliveries = union( attributesToUpdate.unidentifiedDeliveries = union(
previousUnidentifiedDeliveries, previousUnidentifiedDeliveries,
newUnidentifiedDeliveries newUnidentifiedDeliveries
@ -2525,6 +2530,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const dataMessage = await upgradeMessageSchema(withQuoteReference); const dataMessage = await upgradeMessageSchema(withQuoteReference);
const isGroupStoryReply =
isGroup(conversation.attributes) && dataMessage.storyId;
try { try {
const now = new Date().getTime(); const now = new Date().getTime();
@ -2747,6 +2755,17 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
conversation.set(attributes); conversation.set(attributes);
// Sync group story reply expiration timers with the parent story's
// expiration timer
if (isGroupStoryReply && storyQuote) {
message.set({
expireTimer: storyQuote.get('expireTimer'),
expirationStartTimestamp: storyQuote.get(
'expirationStartTimestamp'
),
});
}
if ( if (
dataMessage.expireTimer && dataMessage.expireTimer &&
!isExpirationTimerUpdate(dataMessage) !isExpirationTimerUpdate(dataMessage)
@ -2848,8 +2867,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
const conversationTimestamp = conversation.get('timestamp'); const conversationTimestamp = conversation.get('timestamp');
const isGroupStoryReply =
isGroup(conversation.attributes) && message.get('storyId');
if ( if (
!isStory(message.attributes) && !isStory(message.attributes) &&
!isGroupStoryReply && !isGroupStoryReply &&
@ -3401,17 +3418,23 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
deleteServerTimestamp: del.get('serverTimestamp'), deleteServerTimestamp: del.get('serverTimestamp'),
}); });
// Remove any notifications for this message try {
notificationService.removeBy({ messageId: this.get('id') }); this.deletingForEveryone = true;
// Erase the contents of this message // Remove any notifications for this message
await this.eraseContents( notificationService.removeBy({ messageId: this.get('id') });
{ deletedForEveryone: true, reactions: [] },
shouldPersist
);
// Update the conversation's last message in case this was the last message // Erase the contents of this message
this.getConversation()?.updateLastMessage(); await this.eraseContents(
{ deletedForEveryone: true, reactions: [] },
shouldPersist
);
// Update the conversation's last message in case this was the last message
this.getConversation()?.updateLastMessage();
} finally {
this.deletingForEveryone = undefined;
}
} }
clearNotifications(reaction: Partial<ReactionType> = {}): void { clearNotifications(reaction: Partial<ReactionType> = {}): void {

View file

@ -20,17 +20,19 @@ export async function cleanupMessage(
await deleteMessageData(message); await deleteMessageData(message);
if ( const isGroupConversation = Boolean(
isStory(message) && parentConversation && !isDirectConversation(parentConversation.attributes)
isDirectConversation(parentConversation?.attributes) );
) {
await fixupStoryReplies(conversationId, id); if (isStory(message)) {
await cleanupStoryReplies(conversationId, id, isGroupConversation);
} }
} }
async function fixupStoryReplies( async function cleanupStoryReplies(
conversationId: string, conversationId: string,
storyId: string, storyId: string,
isGroupConversation: boolean,
pagination?: { pagination?: {
messageId: string; messageId: string;
receivedAt: number; receivedAt: number;
@ -42,6 +44,7 @@ async function fixupStoryReplies(
conversationId, conversationId,
{ {
includeStoryReplies: false, includeStoryReplies: false,
messageId,
receivedAt, receivedAt,
storyId, storyId,
} }
@ -59,13 +62,27 @@ async function fixupStoryReplies(
return; return;
} }
replies.forEach(reply => { if (isGroupConversation) {
const model = window.MessageController.register(reply.id, reply); // Cleanup all group replies
model.unset('storyReplyContext'); await Promise.all(
model.hydrateStoryContext(null); replies.map(reply => {
}); const replyMessageModel = window.MessageController.register(
reply.id,
reply
);
return replyMessageModel.eraseContents();
})
);
} else {
// Refresh the storyReplyContext data for 1:1 conversations
replies.forEach(reply => {
const model = window.MessageController.register(reply.id, reply);
model.unset('storyReplyContext');
model.hydrateStoryContext(null);
});
}
return fixupStoryReplies(conversationId, storyId, { return cleanupStoryReplies(conversationId, storyId, isGroupConversation, {
messageId: lastMessageId, messageId: lastMessageId,
receivedAt: lastReceivedAt, receivedAt: lastReceivedAt,
}); });
@ -76,6 +93,16 @@ export async function deleteMessageData(
): Promise<void> { ): Promise<void> {
await window.Signal.Migrations.deleteExternalMessageFiles(message); await window.Signal.Migrations.deleteExternalMessageFiles(message);
if (isStory(message)) {
const { id, conversationId } = message;
const parentConversation =
window.ConversationController.get(conversationId);
const isGroupConversation = Boolean(
parentConversation && !isDirectConversation(parentConversation.attributes)
);
await cleanupStoryReplies(conversationId, id, isGroupConversation);
}
const { sticker } = message; const { sticker } = message;
if (!sticker) { if (!sticker) {
return; return;

View file

@ -12,6 +12,10 @@ export async function deleteForEveryone(
doe: DeleteModel, doe: DeleteModel,
shouldPersist = true shouldPersist = true
): Promise<void> { ): Promise<void> {
if (message.deletingForEveryone || message.get('deletedForEveryone')) {
return;
}
if (isDeletionByMe(message, doe)) { if (isDeletionByMe(message, doe)) {
await message.handleDeleteForEveryone(doe, shouldPersist); await message.handleDeleteForEveryone(doe, shouldPersist);
return; return;