Support for message retry requests
This commit is contained in:
parent
28f016ce48
commit
ee513a1965
37 changed files with 1996 additions and 359 deletions
|
@ -139,6 +139,8 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
throttledFetchSMSOnlyUUID?: () => Promise<void> | void;
|
||||
|
||||
throttledMaybeMigrateV1Group?: () => Promise<void> | void;
|
||||
|
||||
typingRefreshTimer?: NodeJS.Timer | null;
|
||||
|
||||
typingPauseTimer?: NodeJS.Timer | null;
|
||||
|
@ -304,7 +306,11 @@ export class ConversationModel extends window.Backbone
|
|||
this.isFetchingUUID = this.isSMSOnly();
|
||||
|
||||
this.throttledFetchSMSOnlyUUID = window._.throttle(
|
||||
this.fetchSMSOnlyUUID,
|
||||
this.fetchSMSOnlyUUID.bind(this),
|
||||
FIVE_MINUTES
|
||||
);
|
||||
this.throttledMaybeMigrateV1Group = window._.throttle(
|
||||
this.maybeMigrateV1Group.bind(this),
|
||||
FIVE_MINUTES
|
||||
);
|
||||
|
||||
|
@ -811,6 +817,10 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
setRegistered(): void {
|
||||
if (this.get('discoveredUnregisteredAt') === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.log.info(
|
||||
`Conversation ${this.idForLogging()} is registered once again`
|
||||
);
|
||||
|
@ -1193,15 +1203,18 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
);
|
||||
|
||||
const {
|
||||
ContentHint,
|
||||
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
|
||||
const sendOptions = await this.getSendOptions();
|
||||
if (this.isPrivate()) {
|
||||
const silent = true;
|
||||
this.wrapSend(
|
||||
window.textsecure.messaging.sendMessageProtoAndWait(
|
||||
timestamp,
|
||||
groupMembers,
|
||||
contentMessage,
|
||||
silent,
|
||||
ContentHint.SUPPLEMENTARY,
|
||||
undefined,
|
||||
{
|
||||
...sendOptions,
|
||||
online: true,
|
||||
|
@ -1211,6 +1224,7 @@ export class ConversationModel extends window.Backbone
|
|||
} else {
|
||||
this.wrapSend(
|
||||
window.Signal.Util.sendContentMessageToGroup({
|
||||
contentHint: ContentHint.SUPPLEMENTARY,
|
||||
contentMessage,
|
||||
conversation: this,
|
||||
online: true,
|
||||
|
@ -2438,7 +2452,8 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
async addChatSessionRefreshed(receivedAt: number): Promise<void> {
|
||||
window.log.info(
|
||||
`addChatSessionRefreshed: adding for ${this.idForLogging()}`
|
||||
`addChatSessionRefreshed: adding for ${this.idForLogging()}`,
|
||||
{ receivedAt }
|
||||
);
|
||||
|
||||
const message = ({
|
||||
|
@ -2466,6 +2481,43 @@ export class ConversationModel extends window.Backbone
|
|||
this.trigger('newmessage', model);
|
||||
}
|
||||
|
||||
async addDeliveryIssue(
|
||||
receivedAt: number,
|
||||
senderUuid: string
|
||||
): Promise<void> {
|
||||
window.log.info(`addDeliveryIssue: adding for ${this.idForLogging()}`, {
|
||||
receivedAt,
|
||||
senderUuid,
|
||||
});
|
||||
|
||||
const message = ({
|
||||
conversationId: this.id,
|
||||
type: 'delivery-issue',
|
||||
sourceUuid: senderUuid,
|
||||
sent_at: receivedAt,
|
||||
received_at: window.Signal.Util.incrementMessageCounter(),
|
||||
received_at_ms: receivedAt,
|
||||
unread: 1,
|
||||
// TODO: DESKTOP-722
|
||||
// this type does not fully implement the interface it is expected to
|
||||
} as unknown) as typeof window.Whisper.MessageAttributesType;
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(message, {
|
||||
Message: window.Whisper.Message,
|
||||
});
|
||||
const model = window.MessageController.register(
|
||||
id,
|
||||
new window.Whisper.Message({
|
||||
...message,
|
||||
id,
|
||||
})
|
||||
);
|
||||
|
||||
this.trigger('newmessage', model);
|
||||
|
||||
await this.notify(model);
|
||||
}
|
||||
|
||||
async addKeyChange(keyChangedId: string): Promise<void> {
|
||||
window.log.info(
|
||||
'adding key change advisory for',
|
||||
|
@ -3108,6 +3160,10 @@ export class ConversationModel extends window.Backbone
|
|||
profileKey = await ourProfileKeyService.get();
|
||||
}
|
||||
|
||||
const {
|
||||
ContentHint,
|
||||
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
|
||||
|
||||
if (this.isPrivate()) {
|
||||
return window.textsecure.messaging.sendMessageToIdentifier(
|
||||
destination,
|
||||
|
@ -3120,6 +3176,8 @@ export class ConversationModel extends window.Backbone
|
|||
targetTimestamp,
|
||||
timestamp,
|
||||
undefined, // expireTimer
|
||||
ContentHint.SUPPLEMENTARY,
|
||||
undefined, // groupId
|
||||
profileKey,
|
||||
options
|
||||
);
|
||||
|
@ -3134,6 +3192,7 @@ export class ConversationModel extends window.Backbone
|
|||
profileKey,
|
||||
},
|
||||
this,
|
||||
ContentHint.SUPPLEMENTARY,
|
||||
options
|
||||
);
|
||||
})();
|
||||
|
@ -3254,6 +3313,9 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
const options = await this.getSendOptions();
|
||||
const {
|
||||
ContentHint,
|
||||
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
|
||||
|
||||
const promise = (() => {
|
||||
if (this.isPrivate()) {
|
||||
|
@ -3268,6 +3330,8 @@ export class ConversationModel extends window.Backbone
|
|||
undefined, // deletedForEveryoneTimestamp
|
||||
timestamp,
|
||||
expireTimer,
|
||||
ContentHint.SUPPLEMENTARY,
|
||||
undefined, // groupId
|
||||
profileKey,
|
||||
options
|
||||
);
|
||||
|
@ -3285,6 +3349,7 @@ export class ConversationModel extends window.Backbone
|
|||
profileKey,
|
||||
},
|
||||
this,
|
||||
ContentHint.SUPPLEMENTARY,
|
||||
options
|
||||
);
|
||||
})();
|
||||
|
@ -3492,6 +3557,9 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
const conversationType = this.get('type');
|
||||
const options = await this.getSendOptions();
|
||||
const {
|
||||
ContentHint,
|
||||
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
|
||||
|
||||
let promise;
|
||||
if (conversationType === Message.GROUP) {
|
||||
|
@ -3510,6 +3578,7 @@ export class ConversationModel extends window.Backbone
|
|||
mentions,
|
||||
},
|
||||
this,
|
||||
ContentHint.RESENDABLE,
|
||||
options
|
||||
);
|
||||
} else {
|
||||
|
@ -3524,6 +3593,8 @@ export class ConversationModel extends window.Backbone
|
|||
undefined, // deletedForEveryoneTimestamp
|
||||
now,
|
||||
expireTimer,
|
||||
ContentHint.RESENDABLE,
|
||||
undefined, // groupId
|
||||
profileKey,
|
||||
options
|
||||
);
|
||||
|
|
|
@ -41,6 +41,7 @@ import {
|
|||
import { PropsData as SafetyNumberNotificationProps } from '../components/conversation/SafetyNumberNotification';
|
||||
import { PropsData as VerificationNotificationProps } from '../components/conversation/VerificationNotification';
|
||||
import { PropsDataType as GroupV1MigrationPropsType } from '../components/conversation/GroupV1Migration';
|
||||
import { PropsDataType as DeliveryIssuePropsType } from '../components/conversation/DeliveryIssueNotification';
|
||||
import {
|
||||
PropsData as GroupNotificationProps,
|
||||
ChangeType,
|
||||
|
@ -132,6 +133,10 @@ type MessageBubbleProps =
|
|||
type: 'chatSessionRefreshed';
|
||||
data: null;
|
||||
}
|
||||
| {
|
||||
type: 'deliveryIssue';
|
||||
data: DeliveryIssuePropsType;
|
||||
}
|
||||
| {
|
||||
type: 'message';
|
||||
data: PropsForMessage;
|
||||
|
@ -407,6 +412,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
data: null,
|
||||
};
|
||||
}
|
||||
if (this.isDeliveryIssue()) {
|
||||
return {
|
||||
type: 'deliveryIssue',
|
||||
data: this.getPropsForDeliveryIssue(),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
|
@ -581,6 +592,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return this.get('type') === 'chat-session-refreshed';
|
||||
}
|
||||
|
||||
isDeliveryIssue(): boolean {
|
||||
return this.get('type') === 'delivery-issue';
|
||||
}
|
||||
|
||||
isProfileChange(): boolean {
|
||||
return this.get('type') === 'profile-change';
|
||||
}
|
||||
|
@ -874,6 +889,14 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
}
|
||||
|
||||
getPropsForDeliveryIssue(): DeliveryIssuePropsType {
|
||||
const sender = this.getContact()?.format();
|
||||
|
||||
return {
|
||||
sender,
|
||||
};
|
||||
}
|
||||
|
||||
getPropsForProfileChange(): ProfileChangeNotificationPropsType {
|
||||
const change = this.get('profileChange');
|
||||
const changedId = this.get('changedId');
|
||||
|
@ -1359,6 +1382,13 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
getNotificationData(): { emoji?: string; text: string } {
|
||||
if (this.isDeliveryIssue()) {
|
||||
return {
|
||||
emoji: '⚠️',
|
||||
text: window.i18n('DeliveryIssue--preview'),
|
||||
};
|
||||
}
|
||||
|
||||
if (this.isChatSessionRefreshed()) {
|
||||
return {
|
||||
emoji: '🔁',
|
||||
|
@ -1893,6 +1923,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
// Rendered sync messages
|
||||
const isCallHistory = this.isCallHistory();
|
||||
const isChatSessionRefreshed = this.isChatSessionRefreshed();
|
||||
const isDeliveryIssue = this.isDeliveryIssue();
|
||||
const isGroupUpdate = this.isGroupUpdate();
|
||||
const isGroupV2Change = this.isGroupV2Change();
|
||||
const isEndSession = this.isEndSession();
|
||||
|
@ -1922,6 +1953,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
// Rendered sync messages
|
||||
isCallHistory ||
|
||||
isChatSessionRefreshed ||
|
||||
isDeliveryIssue ||
|
||||
isGroupUpdate ||
|
||||
isGroupV2Change ||
|
||||
isEndSession ||
|
||||
|
@ -2216,6 +2248,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
let promise;
|
||||
const options = await conversation.getSendOptions();
|
||||
|
||||
const {
|
||||
ContentHint,
|
||||
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
|
||||
|
||||
if (conversation.isPrivate()) {
|
||||
const [identifier] = recipients;
|
||||
promise = window.textsecure.messaging.sendMessageToIdentifier(
|
||||
|
@ -2229,6 +2265,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
this.get('deletedForEveryoneTimestamp'),
|
||||
this.get('sent_at'),
|
||||
this.get('expireTimer'),
|
||||
ContentHint.RESENDABLE,
|
||||
undefined, // groupId
|
||||
profileKey,
|
||||
options
|
||||
);
|
||||
|
@ -2271,6 +2309,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
groupV1,
|
||||
},
|
||||
conversation,
|
||||
ContentHint.RESENDABLE,
|
||||
options,
|
||||
partialSend
|
||||
);
|
||||
|
@ -2403,7 +2442,13 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
async resend(identifier: string): Promise<void | null | Array<void>> {
|
||||
const error = this.removeOutgoingErrors(identifier);
|
||||
if (!error) {
|
||||
window.log.warn('resend: requested number was not present in errors');
|
||||
window.log.warn(
|
||||
'resend: requested number was not present in errors. continuing.'
|
||||
);
|
||||
}
|
||||
|
||||
if (this.isErased()) {
|
||||
window.log.warn('resend: message is erased; refusing to resend');
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2431,7 +2476,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
body,
|
||||
deletedForEveryoneTimestamp: this.get('deletedForEveryoneTimestamp'),
|
||||
expireTimer: this.get('expireTimer'),
|
||||
// flags
|
||||
mentions: this.get('bodyRanges'),
|
||||
preview: previewWithData,
|
||||
profileKey,
|
||||
|
@ -2444,22 +2488,59 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return this.sendSyncMessageOnly(dataMessage);
|
||||
}
|
||||
|
||||
const {
|
||||
ContentHint,
|
||||
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
|
||||
const parentConversation = this.getConversation();
|
||||
const groupId = parentConversation?.get('groupId');
|
||||
const {
|
||||
wrap,
|
||||
sendOptions,
|
||||
} = await window.ConversationController.prepareForSend(identifier);
|
||||
const promise = window.textsecure.messaging.sendMessageToIdentifier(
|
||||
identifier,
|
||||
body,
|
||||
const group =
|
||||
groupId && parentConversation?.isGroupV1()
|
||||
? {
|
||||
id: groupId,
|
||||
type: window.textsecure.protobuf.GroupContext.Type.DELIVER,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const timestamp = this.get('sent_at');
|
||||
const contentMessage = await window.textsecure.messaging.getContentMessage({
|
||||
attachments,
|
||||
quoteWithData,
|
||||
previewWithData,
|
||||
stickerWithData,
|
||||
null,
|
||||
this.get('deletedForEveryoneTimestamp'),
|
||||
this.get('sent_at'),
|
||||
this.get('expireTimer'),
|
||||
profileKey,
|
||||
body,
|
||||
expireTimer: this.get('expireTimer'),
|
||||
group,
|
||||
groupV2: parentConversation?.getGroupV2Info(),
|
||||
preview: previewWithData,
|
||||
quote: quoteWithData,
|
||||
mentions: this.get('bodyRanges'),
|
||||
recipients: [identifier],
|
||||
sticker: stickerWithData,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
if (parentConversation) {
|
||||
const senderKeyInfo = parentConversation.get('senderKeyInfo');
|
||||
if (senderKeyInfo && senderKeyInfo.distributionId) {
|
||||
const senderKeyDistributionMessage = await window.textsecure.messaging.getSenderKeyDistributionMessage(
|
||||
senderKeyInfo.distributionId
|
||||
);
|
||||
|
||||
window.dcodeIO.ByteBuffer.wrap(
|
||||
window.Signal.Crypto.typedArrayToArrayBuffer(
|
||||
senderKeyDistributionMessage.serialize()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const promise = window.textsecure.messaging.sendMessageProtoAndWait(
|
||||
timestamp,
|
||||
[identifier],
|
||||
contentMessage,
|
||||
ContentHint.RESENDABLE,
|
||||
groupId && parentConversation?.isGroupV2() ? groupId : undefined,
|
||||
sendOptions
|
||||
);
|
||||
|
||||
|
@ -2506,7 +2587,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
sent_to: _.union(sentTo, result.successfulIdentifiers),
|
||||
sent: true,
|
||||
expirationStartTimestamp: Date.now(),
|
||||
unidentifiedDeliveries: result.unidentifiedDeliveries,
|
||||
unidentifiedDeliveries: _.union(
|
||||
this.get('unidentifiedDeliveries') || [],
|
||||
result.unidentifiedDeliveries
|
||||
),
|
||||
});
|
||||
|
||||
if (!this.doNotSave) {
|
||||
|
@ -2595,7 +2679,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
sent_to: _.union(sentTo, result.successfulIdentifiers),
|
||||
sent: true,
|
||||
expirationStartTimestamp,
|
||||
unidentifiedDeliveries: result.unidentifiedDeliveries,
|
||||
unidentifiedDeliveries: _.union(
|
||||
this.get('unidentifiedDeliveries') || [],
|
||||
result.unidentifiedDeliveries
|
||||
),
|
||||
});
|
||||
promises.push(this.sendSyncMessage());
|
||||
} else if (result.errors) {
|
||||
|
@ -3452,6 +3539,24 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
}
|
||||
|
||||
// Now check for decryption error placeholders
|
||||
const { retryPlaceholders } = window.Signal.Services;
|
||||
if (retryPlaceholders) {
|
||||
const item = await retryPlaceholders.findByMessageAndRemove(
|
||||
conversationId,
|
||||
message.get('sent_at')
|
||||
);
|
||||
if (item) {
|
||||
window.log.info(
|
||||
`handleDataMessage: found retry placeholder. Updating ${message.idForLogging()} received_at/received_at_ms`
|
||||
);
|
||||
message.set({
|
||||
received_at: item.receivedAtCounter,
|
||||
received_at_ms: item.receivedAt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// GroupV2
|
||||
|
||||
if (initialMessage.groupV2) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue