Story send: Send sync message even in partial failure
This commit is contained in:
parent
2d5c154e2a
commit
0e49f7906d
5 changed files with 84 additions and 30 deletions
|
@ -228,6 +228,7 @@ export async function startApp(): Promise<void> {
|
|||
hasStoriesDisabled: window.storage.get('hasStoriesDisabled', false),
|
||||
});
|
||||
window.textsecure.server = server;
|
||||
window.textsecure.messaging = new window.textsecure.MessageSender(server);
|
||||
|
||||
initializeAllJobQueues({
|
||||
server,
|
||||
|
@ -2084,8 +2085,6 @@ export async function startApp(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
window.textsecure.messaging = new window.textsecure.MessageSender(server);
|
||||
|
||||
// Update our profile key in the conversation if we just got linked.
|
||||
const profileKey = await ourProfileKeyService.get();
|
||||
if (firstRun && profileKey) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import { isNotNil } from '../../util/isNotNil';
|
|||
import { isSent } from '../../messages/MessageSendState';
|
||||
import { ourProfileKeyService } from '../../services/ourProfileKey';
|
||||
import { sendContentMessageToGroup } from '../../util/sendToGroup';
|
||||
import { SendMessageChallengeError } from '../../textsecure/Errors';
|
||||
|
||||
export async function sendStory(
|
||||
conversation: ConversationModel,
|
||||
|
@ -55,6 +56,16 @@ export async function sendStory(
|
|||
return;
|
||||
}
|
||||
|
||||
// We can send a story to either:
|
||||
// 1) the current group, or
|
||||
// 2) all selected distribution lists (in queue for our own conversationId)
|
||||
if (!isGroupV2(conversation.attributes) && !isMe(conversation.attributes)) {
|
||||
log.error(
|
||||
'stories.sendStory: Conversation is neither groupV2 nor our own. Cannot send.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to generate the StoryMessage proto once at the top level so we
|
||||
// can reuse it but first we'll need textAttachment | fileAttachment.
|
||||
// This function pulls off the attachment and generates the proto from the
|
||||
|
@ -153,9 +164,11 @@ export async function sendStory(
|
|||
|
||||
let isSyncMessageUpdate = false;
|
||||
|
||||
// Send to all distribution lists
|
||||
await Promise.all(
|
||||
messageIds.map(async messageId => {
|
||||
// Note: We capture errors here so we are sure to wait for every send process to
|
||||
// complete, and so we can send a sync message afterwards if we sent the story
|
||||
// successfully to at least one recipient.
|
||||
const sendResults = await Promise.allSettled(
|
||||
messageIds.map(async (messageId: string): Promise<void> => {
|
||||
const message = await getMessageById(messageId);
|
||||
if (!message) {
|
||||
log.info(
|
||||
|
@ -322,9 +335,8 @@ export async function sendStory(
|
|||
urgent: false,
|
||||
});
|
||||
|
||||
// Do not send sync messages for distribution lists since that's sent
|
||||
// in bulk at the end.
|
||||
message.doNotSendSyncMessage = Boolean(distributionList);
|
||||
// Don't send normal sync messages; a story sync is sent at the end of the process
|
||||
message.doNotSendSyncMessage = true;
|
||||
|
||||
const messageSendPromise = message.send(
|
||||
handleMessageSend(innerPromise, {
|
||||
|
@ -391,6 +403,26 @@ export async function sendStory(
|
|||
}
|
||||
} catch (thrownError: unknown) {
|
||||
const errors = [thrownError, ...messageSendErrors];
|
||||
|
||||
// We need to check for this here because we can only throw one error up to
|
||||
// conversationJobQueue.
|
||||
errors.forEach(error => {
|
||||
if (error instanceof SendMessageChallengeError) {
|
||||
window.Signal.challengeHandler?.register(
|
||||
{
|
||||
conversationId: conversation.id,
|
||||
createdAt: Date.now(),
|
||||
retryAt: error.retryAt,
|
||||
token: error.data?.token,
|
||||
reason:
|
||||
'conversationJobQueue.run(' +
|
||||
`${conversation.idForLogging()}, story, ${timestamp})`,
|
||||
},
|
||||
error.data
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await handleMultipleSendErrors({
|
||||
errors,
|
||||
isFinalAttempt,
|
||||
|
@ -470,21 +502,39 @@ export async function sendStory(
|
|||
});
|
||||
});
|
||||
|
||||
const options = await getSendOptions(conversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
if (storyMessageRecipients.length === 0) {
|
||||
log.warn(
|
||||
'No successful sends; will not send a sync message for this attempt'
|
||||
);
|
||||
} else {
|
||||
const options = await getSendOptions(conversation.attributes, {
|
||||
syncMessage: true,
|
||||
});
|
||||
|
||||
messaging.sendSyncMessage({
|
||||
destination: conversation.get('e164'),
|
||||
destinationUuid: conversation.get('uuid'),
|
||||
storyMessage: originalStoryMessage,
|
||||
storyMessageRecipients,
|
||||
expirationStartTimestamp: null,
|
||||
isUpdate: isSyncMessageUpdate,
|
||||
options,
|
||||
timestamp,
|
||||
urgent: false,
|
||||
await messaging.sendSyncMessage({
|
||||
// Note: these two fields will be undefined if we're sending to a group
|
||||
destination: conversation.get('e164'),
|
||||
destinationUuid: conversation.get('uuid'),
|
||||
storyMessage: originalStoryMessage,
|
||||
storyMessageRecipients,
|
||||
expirationStartTimestamp: null,
|
||||
isUpdate: isSyncMessageUpdate,
|
||||
options,
|
||||
timestamp,
|
||||
urgent: false,
|
||||
});
|
||||
}
|
||||
|
||||
// We can only throw one Error up to conversationJobQueue to fail the send
|
||||
const sendErrors: Array<PromiseRejectedResult> = [];
|
||||
sendResults.forEach(result => {
|
||||
if (result.status === 'rejected') {
|
||||
sendErrors.push(result);
|
||||
}
|
||||
});
|
||||
if (sendErrors.length) {
|
||||
throw sendErrors[0].reason;
|
||||
}
|
||||
}
|
||||
|
||||
function getMessageRecipients({
|
||||
|
|
|
@ -548,7 +548,9 @@ export const getNonGroupStories = createSelector(
|
|||
conversationIdsWithStories: Set<string>
|
||||
): Array<ConversationType> => {
|
||||
return groups.filter(
|
||||
group => !isGroupInStoryMode(group, conversationIdsWithStories)
|
||||
group =>
|
||||
!isGroupV2(group) ||
|
||||
!isGroupInStoryMode(group, conversationIdsWithStories)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -560,8 +562,10 @@ export const getGroupStories = createSelector(
|
|||
conversationLookup: ConversationLookupType,
|
||||
conversationIdsWithStories: Set<string>
|
||||
): Array<ConversationType> => {
|
||||
return Object.values(conversationLookup).filter(conversation =>
|
||||
isGroupInStoryMode(conversation, conversationIdsWithStories)
|
||||
return Object.values(conversationLookup).filter(
|
||||
conversation =>
|
||||
isGroupV2(conversation) &&
|
||||
isGroupInStoryMode(conversation, conversationIdsWithStories)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -40,11 +40,9 @@ export async function sendDeleteForEveryoneMessage(
|
|||
const messageModel = window.MessageController.register(messageId, message);
|
||||
|
||||
const timestamp = Date.now();
|
||||
if (
|
||||
timestamp - targetTimestamp >
|
||||
(deleteForEveryoneDuration || THREE_HOURS)
|
||||
) {
|
||||
throw new Error('Cannot send DOE for a message older than three hours');
|
||||
const maxDuration = deleteForEveryoneDuration || THREE_HOURS;
|
||||
if (timestamp - targetTimestamp > maxDuration) {
|
||||
throw new Error(`Cannot send DOE for a message older than ${maxDuration}`);
|
||||
}
|
||||
|
||||
messageModel.set({
|
||||
|
|
|
@ -156,6 +156,7 @@ export async function sendStoryMessage(
|
|||
attachments,
|
||||
conversationId: ourConversation.id,
|
||||
expireTimer: DAY / SECOND,
|
||||
expirationStartTimestamp: Date.now(),
|
||||
id: UUID.generate().toString(),
|
||||
readStatus: ReadStatus.Read,
|
||||
received_at: incrementMessageCounter(),
|
||||
|
@ -205,7 +206,8 @@ export async function sendStoryMessage(
|
|||
return;
|
||||
}
|
||||
|
||||
const groupTimestamp = timestamp + index;
|
||||
// We want all of these timestamps to be different from the My Story timestamp.
|
||||
const groupTimestamp = timestamp + index + 1;
|
||||
|
||||
const myId = window.ConversationController.getOurConversationIdOrThrow();
|
||||
const sendState = {
|
||||
|
@ -236,6 +238,7 @@ export async function sendStoryMessage(
|
|||
canReplyToStory: true,
|
||||
conversationId,
|
||||
expireTimer: DAY / SECOND,
|
||||
expirationStartTimestamp: Date.now(),
|
||||
id: UUID.generate().toString(),
|
||||
readStatus: ReadStatus.Read,
|
||||
received_at: incrementMessageCounter(),
|
||||
|
|
Loading…
Add table
Reference in a new issue