Delete right away if we already have the message

This commit is contained in:
Josh Perez 2020-07-27 14:15:32 -04:00 committed by Scott Nonnenberg
parent dfa846e4f3
commit 1ad2b175dc
10 changed files with 194 additions and 27 deletions

View File

@ -2288,6 +2288,7 @@
if (data.message.reaction) {
const { reaction } = data.message;
window.log.info('Queuing reaction for', reaction.targetTimestamp);
const reactionModel = Whisper.Reactions.add({
emoji: reaction.emoji,
remove: reaction.remove,
@ -2305,6 +2306,7 @@
if (data.message.delete) {
const { delete: del } = data.message;
window.log.info('Queuing DOE for', del.targetSentTimestamp);
const deleteModel = Whisper.Deletes.add({
targetSentTimestamp: del.targetSentTimestamp,
serverTimestamp: data.serverTimestamp,

View File

@ -11,8 +11,6 @@
(function() {
'use strict';
const ONE_DAY = 24 * 60 * 60 * 1000;
window.Whisper = window.Whisper || {};
Whisper.Deletes = new (Backbone.Collection.extend({
forMessage(message) {
@ -46,6 +44,8 @@
// Do not await, since this can deadlock the queue
fromContact.queueJob(async () => {
window.log.info('Handling DOE for', del.get('targetSentTimestamp'));
const messages = await window.Signal.Data.getMessagesBySentAt(
del.get('targetSentTimestamp'),
{
@ -74,29 +74,12 @@
return;
}
// Make sure the server timestamps for the DOE and the matching message
// are less than one day apart
const delta = Math.abs(
del.get('serverTimestamp') - targetMessage.get('serverTimestamp')
);
if (delta > ONE_DAY) {
window.log.info('Received late DOE. Dropping.', {
fromId: del.get('fromId'),
targetSentTimestamp: del.get('targetSentTimestamp'),
messageServerTimestamp: message.get('serverTimestamp'),
deleteServerTimestamp: del.get('serverTimestamp'),
});
this.remove(del);
return;
}
const message = MessageController.register(
targetMessage.id,
targetMessage
);
await message.handleDeleteForEveryone(del);
await window.Signal.Util.deleteForEveryone(message, del);
this.remove(del);
});

View File

@ -1111,7 +1111,7 @@
isErased() {
return Boolean(this.get('isErased'));
},
async eraseContents(additionalProperties = {}) {
async eraseContents(additionalProperties = {}, shouldPersist = true) {
if (this.get('isErased')) {
return;
}
@ -1139,9 +1139,11 @@
});
this.trigger('content-changed');
if (shouldPersist) {
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
}
},
unload() {
if (this.quotedMessage) {
@ -2583,7 +2585,9 @@
// Does this message have any pending, previously-received associated
// delete for everyone messages?
const deletes = Whisper.Deletes.forMessage(message);
deletes.forEach(del => Whisper.Deletes.onDelete(del, false));
deletes.forEach(del => {
window.Signal.Util.deleteForEveryone(message, del, false);
});
await window.Signal.Data.saveMessage(message.attributes, {
Message: Whisper.Message,
@ -2658,7 +2662,7 @@
}
},
async handleDeleteForEveryone(del) {
async handleDeleteForEveryone(del, shouldPersist = true) {
window.log.info('Handling DOE.', {
fromId: del.get('fromId'),
targetSentTimestamp: del.get('targetSentTimestamp'),
@ -2673,7 +2677,10 @@
Whisper.Notifications.remove(notificationForMessage);
// Erase the contents of this message
await this.eraseContents({ deletedForEveryone: true, reactions: [] });
await this.eraseContents(
{ deletedForEveryone: true, reactions: [] },
shouldPersist
);
// Update the conversation's last message in case this was the last message
this.getConversation().updateLastMessage();

View File

@ -53,11 +53,22 @@
reaction.get('targetTimestamp')
);
if (!targetConversation) {
window.log.info(
'No contact for reaction',
reaction.get('targetAuthorE164'),
reaction.get('targetAuthorUuid'),
reaction.get('targetTimestamp')
);
return;
}
// awaiting is safe since `onReaction` is never called from inside the queue
await targetConversation.queueJob(async () => {
window.log.info(
'Handling reaction for',
reaction.get('targetTimestamp')
);
const messages = await window.Signal.Data.getMessagesBySentAt(
reaction.get('targetTimestamp'),
{

View File

@ -156,6 +156,7 @@
"@storybook/addons": "5.1.11",
"@storybook/react": "5.1.11",
"@types/agent-base": "4.2.0",
"@types/backbone": "1.4.3",
"@types/chai": "4.1.2",
"@types/classnames": "2.2.3",
"@types/config": "0.0.34",

114
ts/model-types.d.ts vendored Normal file
View File

@ -0,0 +1,114 @@
import * as Backbone from 'backbone';
import { ColorType, LocalizerType } from './types/Util';
import { SendOptionsType } from './textsecure/SendMessage';
import { ConversationType } from './state/ducks/conversations';
import { SyncMessageClass } from './textsecure.d';
interface ModelAttributesInterface {
[key: string]: any;
}
type DeletesAttributesType = {
fromId: string;
serverTimestamp: number;
targetSentTimestamp: number;
};
declare class DeletesModelType extends Backbone.Model<DeletesAttributesType> {
forMessage(message: MessageModelType): Array<DeletesModelType>;
onDelete(doe: DeletesAttributesType): Promise<void>;
}
type TaskResultType = any;
type MessageAttributesType = {
id: string;
serverTimestamp: number;
};
declare class MessageModelType extends Backbone.Model<MessageAttributesType> {
id: string;
static updateTimers(): void;
getContact(): ConversationModelType | undefined | null;
getConversation(): ConversationModelType | undefined | null;
getPropsForSearchResult(): any;
getPropsForBubble(): any;
cleanup(): Promise<void>;
handleDeleteForEveryone(
doe: DeletesModelType,
shouldPersist: boolean
): Promise<void>;
}
type ConversationTypeType = 'private' | 'group';
type ConversationAttributesType = {
id: string;
uuid?: string;
e164?: string;
active_at?: number | null;
draft?: string;
groupId?: string;
isArchived?: boolean;
lastMessage?: string;
members?: Array<string>;
needsVerification?: boolean;
profileFamilyName?: string | null;
profileKey?: string | null;
profileName?: string | null;
profileSharing: boolean;
storageID?: string;
type: ConversationTypeType;
unreadCount?: number;
verified?: number;
version: number;
};
declare class ConversationModelType extends Backbone.Model<
ConversationAttributesType
> {
id: string;
cachedProps: ConversationType;
initialPromise: Promise<any>;
applyMessageRequestResponse(
response: number,
options?: { fromSync: boolean }
): void;
cleanup(): Promise<void>;
disableProfileSharing(): void;
getAccepted(): boolean;
getAvatarPath(): string | undefined;
getColor(): ColorType | undefined;
getIsAddedByContact(): boolean;
getName(): string | undefined;
getNumber(): string;
getProfileName(): string | undefined;
getProfiles(): Promise<Array<Promise<void>>>;
getRecipients: () => Array<string>;
getSendOptions(options?: any): SendOptionsType | undefined;
getTitle(): string;
idForLogging(): string;
isVerified(): boolean;
safeGetVerified(): Promise<number>;
setProfileKey(profileKey?: string | null): Promise<void>;
toggleVerified(): Promise<TaskResultType>;
unblock(): boolean | undefined;
updateE164: (e164?: string) => void;
updateLastMessage: () => Promise<void>;
updateUuid: (uuid?: string) => void;
wrapSend: (sendPromise: Promise<any>) => Promise<any>;
}
declare class ConversationModelCollectionType extends Backbone.Collection<
ConversationModelType
> {
resetLookups(): void;
}
declare class MessageModelCollectionType extends Backbone.Collection<
MessageModelType
> {}

View File

@ -0,0 +1,26 @@
import { DeletesModelType, MessageModelType } from '../model-types.d';
const ONE_DAY = 24 * 60 * 60 * 1000;
export async function deleteForEveryone(
message: MessageModelType,
doe: DeletesModelType,
shouldPersist: boolean = true
): Promise<void> {
// Make sure the server timestamps for the DOE and the matching message
// are less than one day apart
const delta = Math.abs(
doe.get('serverTimestamp') - message.get('serverTimestamp')
);
if (delta > ONE_DAY) {
window.log.info('Received late DOE. Dropping.', {
fromId: doe.get('fromId'),
targetSentTimestamp: doe.get('targetSentTimestamp'),
messageServerTimestamp: message.get('serverTimestamp'),
deleteServerTimestamp: doe.get('serverTimestamp'),
});
return;
}
await message.handleDeleteForEveryone(doe, shouldPersist);
}

View File

@ -4,6 +4,7 @@ import { arrayBufferToObjectURL } from './arrayBufferToObjectURL';
import { combineNames } from './combineNames';
import { createBatcher } from './batcher';
import { createWaitBatcher } from './waitBatcher';
import { deleteForEveryone } from './deleteForEveryone';
import { downloadAttachment } from './downloadAttachment';
import { hasExpired } from './hasExpired';
import { isFileDangerous } from './isFileDangerous';
@ -17,6 +18,7 @@ export {
combineNames,
createBatcher,
createWaitBatcher,
deleteForEveryone,
downloadAttachment,
GoogleChrome,
hasExpired,

View File

@ -21,6 +21,7 @@
"prefer-for-of": false,
"no-this-assignment": false,
"binary-expression-operand-order": false,
"no-backbone-get-set-outside-model": false,
// Allows us to write inline `style`s. Revisit when we have a more sophisticated
// CSS-in-JS solution:

View File

@ -2044,6 +2044,14 @@
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
"@types/backbone@1.4.3":
version "1.4.3"
resolved "https://registry.yarnpkg.com/@types/backbone/-/backbone-1.4.3.tgz#75dc6e55382e226788db8d796de346891d6b2256"
integrity sha512-PZVw2FckEbEJ+qh2hvtgpI/4p8yD3sRbA8FEO72k01/90SSH73GcLW3CqcYP5epwDpLl3cKrgK0yypQY4qiuEw==
dependencies:
"@types/jquery" "*"
"@types/underscore" "*"
"@types/body-parser@*":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897"
@ -2217,6 +2225,13 @@
dependencies:
"@types/node" "*"
"@types/jquery@*":
version "3.5.0"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.0.tgz#ccb7dfd317d02d4227dd3803c75297d0c10dad68"
integrity sha512-C7qQUjpMWDUNYQRTXsP5nbYYwCwwgy84yPgoTT7fPN69NH92wLeCtFaMsWeolJD1AF/6uQw3pYt62rzv83sMmw==
dependencies:
"@types/sizzle" "*"
"@types/jquery@3.3.29":
version "3.3.29"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd"
@ -2496,6 +2511,11 @@
dependencies:
source-map "^0.6.1"
"@types/underscore@*":
version "1.10.14"
resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.10.14.tgz#a2a831c72a12deddaef26028d16a5aa48aadbee0"
integrity sha512-VE20ZYf38nmOU1lU0wpQBWcGPlskfKK8uU8AN1UIz5PjxT2YM7HTF0iUA85iGJnbQ3tZweqIfQqmLgLMtP27YQ==
"@types/uuid@3.4.4":
version "3.4.4"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.4.tgz#7af69360fa65ef0decb41fd150bf4ca5c0cefdf5"