Delete right away if we already have the message
This commit is contained in:
parent
dfa846e4f3
commit
1ad2b175dc
10 changed files with 194 additions and 27 deletions
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
|
||||
await window.Signal.Data.saveMessage(this.attributes, {
|
||||
Message: Whisper.Message,
|
||||
});
|
||||
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();
|
||||
|
|
|
@ -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'),
|
||||
{
|
||||
|
|
|
@ -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
114
ts/model-types.d.ts
vendored
Normal 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
|
||||
> {}
|
26
ts/util/deleteForEveryone.ts
Normal file
26
ts/util/deleteForEveryone.ts
Normal 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);
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue